//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
// 

#include "Peer.h"
#include "BitTorrentFile.h"
#include "BTStatistics.h"
#include "Tracker.h"
#include <utility>
#include <cassert>
#include <algorithm>
#include <cmath>
#include <exception>

namespace peermaliziosi {

using namespace std;


Tracker * Peer::tracker= new Tracker();

Define_Module(Peer);

Peer::Peer() {

}

Peer::~Peer() {
}

// // //
// METODI DI OMNeT++

void Peer::finish() {


    handleLeaveSwarm(NULL); // distruggo le strutture solo di questo peer

    if (peerID==numPeers-1) {
        // stampa statistiche costruttore/distruttore
        EV << "Peer "<< peerID << ", bilancio (#costruttori - #distruttori), zero significa tutto ok:" << endl;
        EV << "FileBlock: " << (FileBlock::countConstructors - FileBlock::countDestructors) << endl;
        EV << "FilePiece: " << (FilePiece::countConstructors - FilePiece::countDestructors) << endl;
        EV << "BitTorrentFile: " << (BitTorrentFile::countConstructors - BitTorrentFile::countDestructors) << endl;
        EV << "RateStruct: " << (RateStruct::countConstructors - RateStruct::countDestructors) << endl;
        EV << "MsgStruct: " << (MsgStruct::countConstructors - MsgStruct::countDestructors) << endl;
        EV << "PieceScore: " << (PieceScore::countConstructors - PieceScore::countDestructors) << endl;
        EV << "RemotePeer: " << (RemotePeer::countConstructors - RemotePeer::countDestructors) << endl;
    }

#ifndef NO_COUT
    if (peerID==numPeers-1) {
        // il cout si puo' rimuovere senza problemi
        // e' stato aggiunto solo per vedere direttamente la stampa senza dover aprire il file di log
        cout << "Peer "<< peerID << ", bilancio (#costruttori - #distruttori), zero significa tutto ok:" << endl;
        cout << "FileBlock: " << (FileBlock::countConstructors - FileBlock::countDestructors) << endl;
        cout << "FilePiece: " << (FilePiece::countConstructors - FilePiece::countDestructors) << endl;
        cout << "BitTorrentFile: " << (BitTorrentFile::countConstructors - BitTorrentFile::countDestructors) << endl;
        cout << "RateStruct: " << (RateStruct::countConstructors - RateStruct::countDestructors) << endl;
        cout << "MsgStruct: " << (MsgStruct::countConstructors - MsgStruct::countDestructors) << endl;
        cout << "PieceScore: " << (PieceScore::countConstructors - PieceScore::countDestructors) << endl;
        cout << "RemotePeer: " << (RemotePeer::countConstructors - RemotePeer::countDestructors) << endl;
        cout.flush();
    }
#endif
}

void Peer::initialize()
{
	cModule *sysM= simulation.getSystemModule();
    numPeers= sysM->par("numPeers").longValue(); // numero di peer
    peerID= getIndex();
    active= false;
    //optPeer=-1;
    maliciousEnabled= par("endGameEnabled").boolValue();
    outgoingMsg= NULL;
    incomingMsg= NULL;
    startKeepAlive= NULL;
    trackerRequest=NULL;
    myFile= NULL;
    chokingCount=0;
    unchokes_since_last=0;

    lastPieceSel=-1;

    newSeederSignal = registerSignal("newSeeder");
    newDownloaderSignal = registerSignal("newDownloader");
    seederTimeSignal = registerSignal("seederTime");
    blocksSentSignal = registerSignal("blocksSent");
    fileCompletationSignal = registerSignal("fileCompletation");
    duplicatedBlocksSignal = registerSignal("duplicatedBlocks");
    effectiveCancelsSignal = registerSignal("effectiveCancels");


    numRequestSentSignal=registerSignal("numRequestSent");
    numPieceSentSignal=registerSignal("numPieceSent");
    numCancelSentSignal=registerSignal("numCancelSent");

    numRequestForMeSignal=registerSignal("numRequestForMe");
    numPieceForMeSignal=registerSignal("numPieceForMe");
    numCancelForMeSignal=registerSignal("numCancelForMe");


	// posizione
    float pi= 3.14159265; // pi greco
    float radius= 250.0; // distanza fra il centro e i peer    
    float nPeer= (float) numPeers; // numero di peer
    float angle= ((float)peerID / nPeer)*2*pi; // angolo fra 0 e 2*pi
    int x= cos(angle)*radius; // coordinata x
    x= x + 300; // traslazione rispetto al centro di coordinate (300,300)
    int y= sin(angle)*radius;
    y= y + 300; // traslazione rispetto al centro di coordinate (300,300)

    getDisplayString().setTagArg("i", 0, "block/routing");
    getDisplayString().setTagArg("i", 1, "white");
    getDisplayString().setTagArg("p", 0, x);
    getDisplayString().setTagArg("p", 1, y);
    // equivale a: "i=block/routing,white;p=%x,%y"

    simtime_t delta= par("joinDelay");
    JoinSwarm *join= new JoinSwarm();
    const char *strJoin;
    bool emptyRandom= ( 1==bernoulli(par("initJoinEmptyRandom").doubleValue()) );
    if (emptyRandom) {
        // inizio vuoto
        join->setEmpty(true);
        join->setAgainRandom(false); // non significativo in questo caso
        strJoin= "JoinSwarm-vuoto";
    } else {
        // inizio inizializzato a random
        join->setEmpty(false);
        join->setAgainRandom(false);
        strJoin= "JoinSwarm-random";
    }
    join->setName(strJoin);
    scheduleAt(simTime()+delta, join);

}

void Peer::handleMessage(cMessage *msg) {
    // che bruttura...

#ifndef NO_COUT
	// anche se va in crash, su stdout si ha la stampa del numero di eventi
    cout << "> Peer " << peerID <<  ", evento " << simulation.getEventNumber()
            <<", msgName " << msg->getName() << "\n";
    cout.flush();
#endif

    if(BitTorrentMsg *tmsg = dynamic_cast<BitTorrentMsg *>(msg)){
        if (active) {
            if(dynamic_cast<Request *>(msg)){
                pushIncomingMsg(tmsg);
            }
            else{
                // i pacchetti che non sono richieste sono processate immediatamente
                handleBitTorrentMessage(tmsg);
            }
        } else {
            delete msg;
        }
    } else if (InternalMsg *imsg = dynamic_cast<InternalMsg *>(msg)) {
        handleInternalMessage(imsg);
    } else {
        EV << "Peer " << peerID << ": messaggio " << (msg->getName()) << " non gestito\n\n";
    }
}

// // //
// GESTIONE DELLE CODE DEI MESSAGGI DI BITTORRENT

void Peer::pushIncomingMsg(BitTorrentMsg *msg) {

    if (incoming.size()==0 && (incomingMsg==NULL)) {
        // non ci sono messaggi
        incomingMsg= new ExtractIncomingMsg("ExtractIncomingMsg");
        simtime_t delay= par("incomingServingTime");
        scheduleAt(simTime()+delay, incomingMsg);
    }
    MsgStruct *msgStruct= new MsgStruct();
    if(Request *req = dynamic_cast<Request *>(msg)){
        msgStruct->isRequest= true;
        msgStruct->blockIndex= req->getBlockIndex();
        msgStruct->pieceIndex= req->getPieceIndex();
        msgStruct->sourcePeer=msg->getSource();
    	msgStruct->pushMsg(msg);
    	incoming.push_back(msgStruct);
    } else {
        //vengono messe in coda solo le richieste
        EV << "Peer " << peerID << ": messaggio " << (msg->getName()) << " non e' una richiesta, NON DEVE essere messo nella coda incoming\n\n";
    }

    

}

BitTorrentMsg * Peer::popIncomingMsg() {

    BitTorrentMsg *msg;
    MsgStruct *msgStruct;


    msgStruct= incoming.front();
#ifndef NO_COUT
    cout << "> Peer " << peerID << ": Sto per estrarre il messaggio incoming " << msgStruct->getMsg()->getName() << "\n";
    cout.flush();
#endif
    incoming.pop_front();
    msg= msgStruct->popMsg();
    delete msgStruct;

    if (incoming.size()>0) {
        // ci sono ancora messaggi => schedulo una nuova notifica
        incomingMsg= new ExtractIncomingMsg("ExtractIncomingMsg");
        simtime_t delay= par("incomingServingTime");
        scheduleAt(simTime()+delay, incomingMsg);
    } else {
        incomingMsg= NULL;
    }

    return msg;
}

void Peer::pushOutgoingMsg(BitTorrentMsg *msg) {


    // inoltro al destinatario
    int dest= msg->getDestination();

    cChannel *txChannel = getTransmissionChannel(dest);
    simtime_t txFinishTime = txChannel->getTransmissionFinishTime(); // tempo al quale posso inviare
    EV << "TransmissionFinishTime: " << txFinishTime << ", now: " << simTime() << endl;

    if ( outgoingMsg==NULL ) {
        EV << "Inoltro (senza passare dalla coda) \"" << msg->getName() << "\" al peer " << dest << endl;;
        // TODO assert rimovibili
        assert(txFinishTime <= simTime()); // canale libero
        assert(outgoing.empty()); // come e' possibile che il canale sia libero ma esistono pkt in coda di uscita? non puo' essere
        assert(!txChannel->isBusy()); // canale libero
        // channel free; send out packet immediately
        mySend(msg);

        if(dynamic_cast<Request*>(msg)!=NULL){

           numRequestSent++;
           emit(numRequestSentSignal, numRequestSent);

        }

        if(dynamic_cast<Cancel*>(msg)!=NULL){

           numCancelSent++;
           emit(numCancelSentSignal, numCancelSent);

        }

        else if(dynamic_cast<Piece*>(msg)!=NULL){


            numPieceSent++;
            emit(numPieceSentSignal, numPieceSent);

            Piece * pmsg=dynamic_cast<Piece*>(msg);
            // incremento il rate di download del peer

            (othersPeer[dest]->downloadRate.back())++;

            EV << "Peer " << peerID << ": ho incrementato il download rate del peer " << dest << " (download rate = " << (othersPeer[dest]->downloadRate.back()) << ")\n";


            list<Request*> * queue = &(othersPeer[dest]->requestsQueue);


            //rimuovo dalle richieste che mi ha fatto il peer

            for (list<Request*>::iterator it = (*queue).begin(); it!=(*queue).end();){


                if(((*it)->getPieceIndex()==pmsg->getPieceIndex()) && ((*it)->getBlockIndex()==pmsg->getBlockIndex())) {

                        EV << "Peer " << peerID << ": ho trovato la richiesta (" << ((*it)->getPieceIndex()) <<
                                ", " << ((*it)->getBlockIndex()) <<") di quel peer nella relativa coda requestsQueue e l'ho rimossa\n";

                        // rimuovo dalla coda delle richieste del peer remoto
                        delete (*it);
                        it=(*queue).erase(it);
                        break;

                } else {

                    ++it;
                }

            }

        }


    } else {
        // store packet and schedule timer; when the timer expires,
        // the packet should be removed from the queue and sent out

        EV << "Aggiungo \"" << msg->getName() << "\" alla coda di uscita" << endl;
        outgoing.push_back(msg);

        string str= outgoingStr();
        EV << "outgoing (size " << outgoing.size() << "): " << str << endl;
    }

    if (outgoingMsg==NULL) {
        // non ci sono messaggi ExtractOutgoingMsg schedulati => creo un nuovo msg
        EV << "Schedulo un nuovo messaggio di estrazione dalla coda" << endl;
        outgoingMsg= new ExtractOutgoingMsg("ExtractOutgoingMsg");
        txFinishTime = txChannel->getTransmissionFinishTime();
        scheduleAt(txFinishTime, outgoingMsg);
    }
}

void Peer::popOutgoingMsgAndSend() {

    if (outgoing.empty()) {
        // non ci sono piu' pacchetti da inviare
        outgoingMsg= NULL;
    } else {
        // estrazione dalla coda
        assert(outgoing.size()>0);
        BitTorrentMsg *msg= outgoing.front();

        int dest= msg->getDestination();
        bool mustSend= true;

        if ( (dynamic_cast<Piece*>(msg)!=NULL) && (othersPeer.find(dest)!=othersPeer.end()) ){
            // sto per inviare un pezzo e il destinatario e' ancora presente in othersPeer

            Piece * pmsg=dynamic_cast<Piece*>(msg);
            // incremento il rate di download del peer

            (othersPeer[dest]->downloadRate.back())++;

            EV << "Peer " << peerID << ": ho incrementato il download rate del peer " << dest << " (download rate = " << (othersPeer[dest]->downloadRate.back()) << ")\n";


            list<Request*> * queue = &(othersPeer[dest]->requestsQueue);


            //rimuovo dalle richieste che mi ha fatto il peer

            for (list<Request*>::iterator it = (*queue).begin(); it!=(*queue).end();){


                if(((*it)->getPieceIndex()==pmsg->getPieceIndex()) && ((*it)->getBlockIndex()==pmsg->getBlockIndex())) {

                        EV << "Peer " << peerID << ": ho trovato la richiesta (" << ((*it)->getPieceIndex()) <<
                                ", " << ((*it)->getBlockIndex()) <<") di quel peer nella relativa coda requestsQueue e l'ho rimossa\n";

                        // rimuovo dalla coda delle richieste del peer remoto
                        delete (*it);
                        it=(*queue).erase(it);
                        break;

                } else {

                    ++it;
                }

            }

        } else if ( (dynamic_cast<Piece*>(msg)!=NULL) && (othersPeer.find(dest)==othersPeer.end()) ) {
            // sto per inviare un pezzo e il destinatario NON e' piu' presente in othersPeer
            mustSend= false;
        }

        if (mustSend) {
            EV << "Estraggo dalla coda ed invio \"" << msg->getName() << "\" al peer " << dest << endl;
            mySend(msg); // invio

            if(dynamic_cast<Piece*>(msg)!=NULL){

                numPieceSent++;
                emit(numPieceSentSignal, numPieceSent);
            }
            else if (dynamic_cast<Cancel*>(msg)!=NULL){

                numCancelSent++;
                emit(numCancelSentSignal, numCancelSent);
            }
            else if (dynamic_cast<Request*>(msg)!=NULL){
                 numRequestSent++;
                 emit(numRequestSentSignal, numRequestSent);
            }


        } else {
            EV << "Estraggo dalla coda ma NON invio \"" << msg->getName() << "\" al peer " << dest << " (destinatario non piu' presente in othersPeer)" << endl;
            // se non invio il msg, lo devo distruggere
            delete msg;
        }

        outgoing.pop_front();

        string str= outgoingStr();
        EV << "outgoing (size " << outgoing.size() << "): " << str << endl;

        // in ogni caso schedulo un nuovo msg di estrazione
        cChannel *txChannel = getTransmissionChannel(dest);
        simtime_t txFinishTime = txChannel->getTransmissionFinishTime(); // tempo al quale posso inviare
        EV << "Schedulo un nuovo messaggio di estrazione dalla coda di uscita" << endl;
        outgoingMsg= new ExtractOutgoingMsg("ExtractOutgoingMsg");
        scheduleAt(txFinishTime, outgoingMsg);


    }
}

string& Peer::outgoingStr() {
    string *str= new string("(contenuto della coda omesso)");
    return *str;
    /*
    string *str= new string();
    *str= "[";
    for (list<BitTorrentMsg*>::iterator it=outgoing.begin(); it!=outgoing.end();) {
        *str= *str + "\"" + (*it)->getName() + "\"";
        ++it;
        if ( it != outgoing.end()) {
            *str= *str + ",";
        }
    }
    *str= *str + "]";
    return *str;
    */
}

// // //
// GESTIONE DEI MESSAGGI DI BITTORRENT

bool Peer::isTimeExpired(BitTorrentMsg *msg) {

    if(dynamic_cast<Handshake*>(msg)!=NULL){
        // i msg di handshake sono sempre accettati
        return false;
    } else {
        if ( othersPeer.find(msg->getSource()) != othersPeer.end() ) {
            // il mittente e' presente nella mappa
            return false;
        } else {
            /*
             * Se il mittente non e' presente in othersPeer, significa che questo msg
             * e' arrivato troppo tardi: il peer e' gia' stato rimosso come morto
             */
            return true;
        }
    }
}

void Peer::handleBitTorrentMessage(BitTorrentMsg *tmsg){

    EV << "Ricevuto messaggio " << (tmsg->getName()) << " nel peer " << getIndex() << "\n\n";
    EV << "Durata msg: " << tmsg->getDuration() << endl;
    EV << "Lunghezza msg: " << tmsg->getByteLength() << " byte" << endl;

    if (
        (tmsg->getDestination()==peerID) &&  // msg e' destinato a me
        !isTimeExpired(tmsg) // non e' arrivato oltre il tempo massimo
    ) {

        if(Handshake* handshakeMsg = dynamic_cast<Handshake*>(tmsg)){

            handleHandshake(handshakeMsg);

        } else if(Choke* chokeMsg = dynamic_cast<Choke*>(tmsg)){

            handleChoke(chokeMsg);

        } else if(Unchoke* unchokeMsg = dynamic_cast<Unchoke*>(tmsg)){

            handleUnchoke(unchokeMsg);

        } else if(Have* haveMsg = dynamic_cast<Have*>(tmsg)){

            handleHave(haveMsg);

       } else if(Cancel* cancelMsg = dynamic_cast<Cancel*>(tmsg)){

           handleCancel(cancelMsg);

       } else if(Interested* interestedMsg = dynamic_cast<Interested*>(tmsg)){

            handleInterested(interestedMsg);

       } else if(NotInterested* notInterestedMsg = dynamic_cast<NotInterested*>(tmsg)){

            handleNotInterested(notInterestedMsg);

        } else if(KeepAlive* keepAliveMsg = dynamic_cast<KeepAlive*>(tmsg)){

            handleKeepAlive(keepAliveMsg);

       } else if(Request* requestMsg = dynamic_cast<Request*>(tmsg)){

            handleRequest(requestMsg);

       } else if(Piece* pieceMsg = dynamic_cast<Piece*>(tmsg)){

            handlePiece(pieceMsg);

       }


       if(!par("disableKeepAlive").boolValue() && othersPeer.find(tmsg->getSource())!=othersPeer.end()){
        
         // controllo per sicurezza che il peer sia ancora presente

            // creo un nuovo timeout
            RemotePeer *rPeer= othersPeer[tmsg->getSource()];
            if (rPeer->timeout!=NULL) {
                cancelEvent(rPeer->timeout);
                PeerTimeout *old= rPeer->timeout;
                rPeer->timeout= old->dup();
                delete old;
            } else {
                // il peer remoto ha appena fatto l'handshake, quindi non e' ancora stato schedulato il timeout
                rPeer->timeout= new PeerTimeout("PeerTimeout");
                rPeer->timeout->setRemotePeerID(tmsg->getSource());
            }
            scheduleAt(simTime() + SimTime::parse("120s") , rPeer->timeout);

       }


    } else if (isTimeExpired(tmsg)) {

        EV << "Peer " << peerID << ": e' arrivato un messaggio da un peer che avevo rimosso a causa dello scadere del timeout: ignoro il messaggio\n\n";

    } else{

        EV << "Peer " << peerID << ": mi e' stato consegnato un messaggio non destinato a me (NON DOVREBBE ACCADERE)\n\n";

    }
    // in ogni caso rimuovo
    delete tmsg;
}

void Peer::handleCancel(Cancel *cancelMsg){

    // un peer mi aveva comunicato di essere interessato ad un mio pezzo ed adesso non e' piu' interessato (lo ha gia' ricevuto)

    // rimuovo la sua richiesta dalla relativa coda

    numCancelForMe++;
    emit(numCancelForMeSignal, numCancelForMe);

    int peer= cancelMsg->getSource();

    EV << "Peer " << peerID << ": il peer " << peer << " mi ha inviato un cancel riguardante il blocco "
            << (cancelMsg->getPieceIndex()) << " del pezzo " << (cancelMsg->getBlockIndex()) << "\n";

    if(othersPeer.find(peer)!=othersPeer.end() && (!(othersPeer[peer]->am_choking))){   //se ho soffocato questo peer non mi interessa il suo Cancel, ho gia' rimosso le sue richieste


         list<Request*> * queue = &(othersPeer[peer]->requestsQueue);

        for (list<Request*>::iterator it = (*queue).begin(); it!=(*queue).end();){

            if(((*it)->getPieceIndex()==cancelMsg->getPieceIndex()) && ((*it)->getBlockIndex()==cancelMsg->getBlockIndex())) {
                // ho trovato la precedente richiesta del peer

                EV << "Peer " << peerID << ": ho trovato la richiesta di quel peer nella relativa coda requestsQueue e l'ho rimossa\n";

                // rimuovo dalla coda delle richieste del peer remoto
                delete (*it);
                it=(*queue).erase(it);

                break; // trovato => mi fermo

            } else {

                ++it;
            }
        }


        bool foundIncoming=false;
        // rimuovo dalla coda in ingresso
        for (list<MsgStruct *>::iterator ms = incoming.begin(); ms!=incoming.end();++ms){
            if ( ((*ms)->sourcePeer==peer) && ((*ms)->isRequest) &&((*ms)->pieceIndex == cancelMsg->getPieceIndex()) &&
               ((*ms)->blockIndex == cancelMsg->getBlockIndex()) ) {

                effectiveCancels++;
                emit(effectiveCancelsSignal, effectiveCancels);

                EV << "Peer " << peerID << ": ho trovato la richiesta di quel peer nella coda incoming e l'ho rimossa\n";

                delete (*ms); // distruggo la struttura E l'eventuale messaggio contenuto

                incoming.erase(ms); // rimuovo dalla coda

                foundIncoming=true;

                break;
            }
        }

        if(!foundIncoming){ //se non ho trovato la richiesta nella coda di entrata, probabilmente sto per inviare il pezzo quindi controllo la coda di uscita.
        //rimuovo dalla coda di uscita
            for (list<BitTorrentMsg *>::iterator ms = outgoing.begin(); ms!=outgoing.end();){
                   if (((*ms)->getDestination()==peer) && (dynamic_cast<Piece *>(*ms))){

                       Piece * piece = dynamic_cast<Piece *>(*ms);
                       if((piece->getPieceIndex() == cancelMsg->getPieceIndex()) &&
                               (piece->getBlockIndex() == cancelMsg->getBlockIndex())) {

                                  effectiveCancels++;
                                  emit(effectiveCancelsSignal, effectiveCancels);

                                   EV << "Peer " << peerID << ": ho trovato la richiesta di quel peer nella coda outgoing e l'ho rimossa\n";

                                   delete (*ms); // distruggo il messaggio contenuto
                                   ms = outgoing.erase(ms); // rimuovo dalla coda
                                   break;
                       }
                       else{
                           ++ms; // avanzo
                       }

                    } else {
                           ++ms; // avanzo
                       }

            }
        }

    }

    else {

        EV << "Peer " << peerID << ": scarto il Cancel ricevuto dal peer " << peer <<"\n";

    }

    EV << "\n";


}

void Peer::handleHandshake(Handshake *handshakeMsg){

    // ho ricevuto un handshake

    int peer = handshakeMsg->getSource();

    bool needUpdateInterests = false;

    int bitfieldSize=handshakeMsg->getBitfieldArraySize();

    if(othersPeer.find(peer)==othersPeer.end()){

        if((unsigned)othersPeer.size() == par("maxPeerSet").longValue()){

               EV << "Peer " << peerID << ": Ho rifiutato l'handshake del peer --> " << peer << " sono gia' connesso al numero max di peer\n";

               return;
           }


        EV << "Peer " << peerID << ": Mi e' arrivato l'handshake da un nuovo peer --> " << peer << "\n";

        bool isSeeder=true;

        for(int i=0;i<bitfieldSize;i++){
            if(!(handshakeMsg->getBitfield(i))){ //il peer non ha questo pezzo
               isSeeder=false;
               break;
            }
        }

        if(myFile->haveTorrent() && isSeeder){ //io sono seeder ed il peer remoto che mi ha inviato l'handshake e' seeder anche lui

            EV << "Peer " << peerID << ": Ho rifiutato l'handshake del peer --> " << peer << " perche' siamo entrambi seeder\n";

            return;
        }


#ifndef NO_COUT
        cout << "> Peer " << peerID << ": Ho conosciuto un nuovo peer (" << peer <<"), lo aggiungo ai peer conosciuti ***\n";
        cout << "> Peer " << peerID << ": la lista dei peer che conosco (escluso quello appena conosciuto): \n";
#endif

        EV << "Peer " << peerID << ": la lista dei peer che conosco (escluso quello appena conosciuto): \n";

        for (map<int, RemotePeer *>::iterator i = othersPeer.begin(); i != othersPeer.end(); ++i){

            EV << "" << i->first << "\n";

#ifndef NO_COUT
            cout << "> " << i->first << "\n";
#endif

        }

#ifndef NO_COUT
        cout.flush();
#endif

        //aggiorno la mia mappa locale con il bitfield e l'ID del peer

        RemotePeer * peerInfo = new RemotePeer();


        //inizializzo i campi associati al peer remoto

        peerInfo->btFile= new BitTorrentFile(bitfieldSize);


        for(int i=0;i<bitfieldSize;i++) {
            if(handshakeMsg->getBitfield(i)){ //il peer ha questo pezzo
                peerInfo->btFile->setPiece(i, true); //segno tutti i blocchi del pezzo come in possesso del peer
            }
        }

        EV << "Pezzi del nuovo peer " << peer << ": " << peerInfo->btFile->toString() << endl;

       /*
        * le connessioni con un client iniziano come choked e not interested.
        * Un blocco e' scaricato dal client quando il client e' interessato ad un peer e quel peer non sta soffocando il client
        * Un blocco e' inviato da un client quando il client non sta soffocando il peer e quel peer e' interessato al client.
        * E' importante per il client tenere i suoi peer informati riguardo il suo interesse o meno verso di loro.
        * Questa informazione dovrebbe essere aggiornata anche dai peer che hanno soffocato il client che la invia in modo che essi sono a conoscenza se quel client
        * iniziera' il download quando viene unchoked (e viceversa).
        */

        peerInfo->am_choking=1;
        peerInfo->am_interested=0;
        peerInfo->pending_unchoking=0;
        peerInfo->peer_choking=1;
        peerInfo->peer_interested=0;

        peerInfo->unchoking_time=0;

        // inizializzo i vettori dei rate
        peerInfo-> uploadRate.push_back(0);
        peerInfo-> downloadRate.push_back(0);
        peerInfo-> canceledRate.push_back(0);

        connection_made(handshakeMsg->getPeerID());

        othersPeer.insert(pair<int, RemotePeer*>(handshakeMsg->getPeerID(), peerInfo));

        // inizializzo il timeout
        peerInfo->timeout= NULL; // vedi handleBitTorrentMessage()

        needUpdateInterests = true;

    }

    else{

        //conosco gia' il peer
        // questo accade quando un peer mi manda un handshake perche' e' uscito e poi rientrato e io non me ne sono ancora reso conto
        // perche' il timer relativo a quel peer non e' ancora scaduto



        // potrei avere un bitfield associato al peer che devo aggiornare
        // infatti puo' essere che il peer mi abbia inviato un nuovo bitfield perche'
        // era uscito ed e' rientrato con un bitfield diverso prima che io lo rimuovessi dalla mia mappa

        bool updateBitfield=false;

        for(int i=0;i<bitfieldSize;i++){
            if((handshakeMsg->getBitfield(i)) != (othersPeer[peer]->btFile->havePiece(i))){
               // il bitfield e' diverso
                updateBitfield = true;
                break;
            }
        }

        if(updateBitfield){

            needUpdateInterests=true;

            // aggiorno il bitfield associato al peer

            EV << "Peer " << peerID << ": avevo un bitfield associato a " << peer << " non aggiornato. Lo aggiorno e invio il mio bitfield\n\n";


            for(int i=0;i<bitfieldSize;i++){
               if(handshakeMsg->getBitfield(i)){ //il peer ha questo pezzo
                   othersPeer[peer]->btFile->setPiece(i, true); //segno tutti i blocchi del pezzo come in possesso del peer
               } else{
                   othersPeer[peer]->btFile->setPiece(i, false); //segno tutti i blocchi del pezzo come non in possesso del peer
               }

            }

        }

    }


    if(!(handshakeMsg->getIKnowYou())){ // il peer remoto ci ha chiesto di inviargli il nostro handshake

        //invio al peer il mio handshake
       Handshake * myHandshake = new Handshake();

       myHandshake->setPeerID(peerID);

       myHandshake->setIKnowYou(true);

       myHandshake->setBitfieldArraySize(bitfieldSize);

       for(int i=0;i<bitfieldSize;i++)
           myHandshake->setBitfield(i,myFile->havePiece(i));

       //invio il mio messaggio di handshake al peer che mi ha inviato il suo
       forwardMessage(myHandshake,peer,"Handshake", (bitfieldSize>>3));

    }

    if(needUpdateInterests){
   //aggiorno i peer a cui sono interessato (se non sono un seeder)
        if(!myFile->haveTorrent())
            updateInterests(-1);
    }


    if((updateRate==NULL) && active && (!(othersPeer[peer]->btFile->haveTorrent()))){

            updateRate = new UpdateRate("UpdateRate");
            scheduleAt(simTime()+simTime().parse("10s"), updateRate);


        }

    if((chokingMsg==NULL) && active && (!(othersPeer[peer]->btFile->haveTorrent()))){
        /* se non e' stato schedulato un messaggio ChokingAlgorithm e io sono attivo e
           il peer che mi ha fatto l'handshake non e' un seeder
           devo riavviare il timer (che avevo fermato perche' tutti i peer attivi erano seeder
           (io compreso) oppure perche' nessuno era interessato a me)*/

             EV << "Peer " << peerID <<": ho appena conosciuto il peer " << peer << ". Riattivo l'algoritmo di choking\n";

#ifndef NO_COUT
             cout << "Peer " << peerID << ": handshake da " << peer << " riattiva choking\n";
             cout.flush();
#endif

			round_robin();
            //chokingMsg=new ChokingAlgorithm("ChokingAlgorithm");
            //chokingMsg->setSec(10);
            // riavvio il timer
            //scheduleAt(simTime()+simTime().parse("10s"), chokingMsg);
    }

}


/***
 * Controlla se tutti i peer conosciuti sono seeder (il peer locale compreso)
 */
bool Peer::areAllSeeder(){

    if (!myFile->haveTorrent()){ //non sono seeder

        return false;
    }

    bool allSeeder=true;

    for (map<int,RemotePeer *>::iterator it= othersPeer.begin(); it != othersPeer.end(); ++it) {
            int pID=it->first;

            if (!(othersPeer[pID]->btFile->haveTorrent())){
                allSeeder=false;
                break;
            }
        }

    return allSeeder;


}


/**
 * Controlla se qualche peer conosciuto e' interessato al peer locale
 */
bool Peer::someoneIsInterested(){

    bool interested=false;

        for (map<int,RemotePeer *>::iterator it= othersPeer.begin(); it != othersPeer.end(); ++it) {
                int pID=it->first;

                if (othersPeer[pID]->peer_interested){
                    interested=true;
                    break;
                }
            }

        return interested;

}


void Peer::handleChoke(Choke *chokeMsg){

    int peer=chokeMsg->getSource();


    /*
    * un peer mi ha soffocato, da adesso non devo richiedere blocchi a quel peer e
    * tutte le richieste pendenti (a cui quel peer non ha ancora risposto) devono essere considerate come scartate da quel peer
    */

    EV << "Peer " << peerID << ": il peer " << peer << " mi ha inviato un messaggio di Choking\n";

    othersPeer[peer]->peer_choking=true;

    if(othersPeer[peer]->myRequestedBlocks.size()!=0){ //avevo richiesto dei blocchi al peer remoto

        EV << "Peer " << peerID << ":ho ancora delle richieste a cui quel peer non a mai risposto:\n";


        for (map<int, set<int> >::iterator i = othersPeer[peer]->myRequestedBlocks.begin(); i != othersPeer[peer]->myRequestedBlocks.end(); ++i){

            // itero sui pezzi del peer che mi ha soffocato

            EV << "+ (" << (i->first) << " , ";

            for (set<int>::iterator j = i->second.begin();j!=i->second.end();++j){

                bool found = false;

                // itero sui blocchi del pezzo i

                EV << (*j) << " - ";


                for(map<int, RemotePeer * >::iterator k = othersPeer.begin(); k != othersPeer.end(); ++k){

                    // itero sugli altri peer , il peer k ha il pezzo i e il blocco j ?

                    if(!(k->second->peer_choking)){
                        // se il peer non mi sta soffocando

                        if((k->second->myRequestedBlocks.find(i->first) != k->second->myRequestedBlocks.end())
                                && (k->second->myRequestedBlocks[i->first].find(*j) != k->second->myRequestedBlocks[i->first].end())){
                            //se ho richiesto quel blocco di quel pezzo anche ad un altro peer remoto
                            found=true;
                            break;
                        }


                    }

                }


                if(found==false){
                    /* rimuovo solo il blocco, lascio il pezzo nella mappa in modo che grazie a selectPiece() venga richiesto ancora */
                    requestedBlocks[i->first].erase(*j);
                }

            }



            EV << ")\n";

        }

        othersPeer[peer]->myRequestedBlocks.clear();


        EV << "Peer " << peerID << ": Potrei aver rimosso delle richieste da requestedBlocks, che adesso contiene:\n";

        for (map<int, set<int> >::iterator k =requestedBlocks.begin();k!=requestedBlocks.end();++k){

            EV << "+ (" << (k->first) << " , ";

                        for (set<int>::iterator it = k->second.begin();it!=k->second.end();++it){

                            EV << (*it) << " - ";

                        }

            EV << ")\n";

        }

    }

    EV << "\n";

}


void Peer::handleUnchoke(Unchoke *unchokeMsg){


    // un peer mi ha comunicato che la mia connessione verso di lui non e' piu' soffocata

    int peer=unchokeMsg->getSource();

    EV << "Peer " << peerID << ": il peer " << peer << " mi ha inviato un messaggio di Unchoking\n";

    othersPeer[peer]->peer_choking=false;

    int piece = updateInterests(peer);

    if((othersPeer[peer]->am_interested && !maliciousEnabled) || (othersPeer[peer]->am_interested && maliciousEnabled && piece!=-2)){ //se sono interessato posso inviare le richieste a quel peer

            EV << "Peer " << peerID << ": sono interessato a quel peer quindi gli invio delle richieste\n";

            //int piece = selectPiece(); //seleziono il pezzo in base alla policy

            requestPiece(piece,peer);

    }

    EV << "\n";

}


void Peer::requestPiece(int pieceIndex, int peer){

    if (myFile->haveTorrent()) {
        // sono seeder: niente da fare
        EV << "Peer " << peerID << ": sono un seeder non devo richiedere nessun blocco\n\n";
        return;
    }

    if (pieceIndex==-1) { //politica END_GAME, non peer malizioso

        assert(!maliciousEnabled);

        EV << "Peer " << peerID << ": sono nella politica END_GAME, richiedo i blocchi che mi mancano a tutti "
                "i peer che li possiedono e che non mi stanno soffocando\n";


        // sono nella politica end game
        for (map<int, RemotePeer* >::iterator i = othersPeer.begin(); i != othersPeer.end(); ++i){

            // itero i peer remoti

            if (!i->second->peer_choking) {
                // il peer non mi sta soffocando => invio la richiesta


              for(int j=0; j<myFile->getNumPieces();j++){

                    // itero i pezzi

                    if(!(i->second->btFile->havePiece(j))) //il peer non ha questo pezzo
                        continue;

                    //il peer remoto ha il pezzo

                    if( (!myFile->havePiece(j)) && (i->second->myRequestedBlocks.find(j) == i->second->myRequestedBlocks.end())){

                        // non avevo ancora richiesto nessun blocco di questo pezzo (che non ho) al peer

                        set<int> * blockSet = new set<int>();

                        i->second->myRequestedBlocks.insert(pair<int, set<int> >(j,(*blockSet)));

                    }

                    for (int k = 0; k < myFile->getNumBlocksPerPiece(); ++k) {

                        // itero i blocchi

                        if ( (!(myFile->haveBlock(j,k))) &&
                                (i->second->myRequestedBlocks[j].find(k) == i->second->myRequestedBlocks[j].end()) &&
                                (i->second->btFile->haveBlock(j, k))
                            ) {
                            // non ho questo blocco e non ho ancora richiesto questo blocco a quel peer e quel peer ha il blocco

                            if(requestedBlocks.find(j)==requestedBlocks.end()){
                                // non avevo ancora richiesto questo pezzo

                                set<int> * blockSet = new set<int>();
                                requestedBlocks.insert(pair<int, set<int> >(j,(*blockSet)));
                                requestedBlocks[j].insert(k);

                            }
                            else if (requestedBlocks[j].find(k)==requestedBlocks[j].end()){
                                // non avevo ancora richiesto questo blocco

                                requestedBlocks[j].insert(k);

                            }

                            Request *requestMsg= new Request();
                            requestMsg->setPieceIndex(j);
                            requestMsg->setBlockIndex(k);

                            EV << "Peer " << peerID << ": richiedo il blocco(" << (j) << ", " << (k) << ") anche al peer " << (i->first) << " \n";

                            stringstream strStream;
                            strStream << "Request (" << (j) << ", " << (k) << ")";
                            i->second->myRequestedBlocks[j].insert(k);
                            forwardMessage(requestMsg,i->first, strStream.str().c_str());
                        }
                    }
                }
            }
        }
        EV << "\n";
        return;
    }

    else if (maliciousEnabled) { //end game malizioso


         assert( (pieceIndex!=-1) || (pieceIndex==-5) ); // deve essere un numero di pezzo valido oppure il valore speciale -5


         /*
          * limita richieste per peer fatte in end_game malizioso al
          * caso pessimo di numero di blocchi richiesti ad ogni peer quando si avvia l'end-game
          * nel protocollo BitTorrrent normale  e dati da:
          * max num request per peer * numero di peer conosciuti
          */

         int maxReqLimit=par("maxBlockRequestNum").longValue() * othersPeer.size();


         for (map<int, RemotePeer* >::iterator o = othersPeer.begin(); o != othersPeer.end(); ++o){


             bool stop=false;

             int numPeerReq=0;


            //controllo quante richieste pendenti ho fatto al peer

            for (map<int, set<int> >::iterator k =o->second->myRequestedBlocks.begin();k!=o->second->myRequestedBlocks.end();++k){

               numPeerReq=numPeerReq+k->second.size();

            }

            if(numPeerReq>=maxReqLimit){

                stop=true;
            }

            if (!o->second->peer_choking && !stop) {
                // il peer non mi sta soffocando => invio la richiesta

                map<int, set<int> >::iterator i = requestedBlocks.begin();

                // controllo tutte le richieste in myRequestedBlocks

                while(!stop && i!=requestedBlocks.end()){

                        int pieceNum=i->first;


                        if (!o->second->btFile->havePiece(pieceNum)){ //il peer non possiede questo pezzo
                            i++;
                            continue;
                        }


                        if((!myFile->havePiece(pieceNum)) && (o->second->myRequestedBlocks.find(pieceNum) == o->second->myRequestedBlocks.end())){

                            // non avevo ancora richiesto nessun blocco di questo pezzo (che non ho) al peer

                            set<int> * blockSet = new set<int>();

                            o->second->myRequestedBlocks.insert(pair<int, set<int> >(pieceNum,(*blockSet)));

                        }

                        for (set<int>::iterator j = i->second.begin();j!=i->second.end();++j){

                            // itero i blocchi

                            if ( (!(myFile->haveBlock(pieceNum,*j))) &&
                                    (o->second->myRequestedBlocks[pieceNum].find(*j) == o->second->myRequestedBlocks[pieceNum].end()) &&
                                    (o->second->btFile->haveBlock(pieceNum,*j))
                                ) {
                                // non ho questo blocco e non ho ancora richiesto questo blocco a quel peer e quel peer ha il blocco


                                Request *requestMsg= new Request();
                                requestMsg->setPieceIndex(pieceNum);
                                requestMsg->setBlockIndex(*j);
                                numPeerReq++;

                                EV << "Peer " << peerID << ": richiedo il blocco(" << (pieceNum) << ", " << (*j) << ") al peer " << (o->first) << " \n";

                                stringstream strStream;
                                strStream << "Request (" << (pieceNum) << ", " << (*j) << ")";
                                o->second->myRequestedBlocks[pieceNum].insert(*j);
                                forwardMessage(requestMsg,o->first, strStream.str().c_str());


                                if(numPeerReq>=maxReqLimit){
                                    stop=true;
                                    break;
                                }
                            }
                        }

                        ++i;

                }


                if(!stop && pieceIndex!=-5){ //richiedo i blocchi del pezzo restituito dalla policy (deve essere un indice di pezzo valido)

					EV << "Devo richiedere il pezzo " << pieceIndex << " con l'END_GAME malizioso\n";

                    int pieceNum=pieceIndex;


                    if (!o->second->btFile->havePiece(pieceNum)){ //il peer non possiede questo pezzo
                        continue; //controllo il prossimo peer
                     }

                    for(int j=0;j<myFile->getNumBlocksPerPiece();j++){




                                if((!myFile->havePiece(pieceNum)) && (o->second->myRequestedBlocks.find(pieceNum) == o->second->myRequestedBlocks.end())){

                                    // non avevo ancora richiesto nessun blocco di questo pezzo (che non ho) al peer

                                    set<int> * blockSet = new set<int>();

                                    o->second->myRequestedBlocks.insert(pair<int, set<int> >(pieceNum,(*blockSet)));

                                }


                                if ((!(myFile->haveBlock(pieceNum,j))) &&
                                        (o->second->myRequestedBlocks[pieceNum].find(j) == o->second->myRequestedBlocks[pieceNum].end()) &&
                                        (o->second->btFile->haveBlock(pieceNum,j))
                                    ) {

                                    // non ho questo blocco e non ho ancora richiesto questo blocco a quel peer e quel peer ha il blocco


                                    if(requestedBlocks.find(pieceNum)==requestedBlocks.end()){
                                        // non avevo ancora richiesto questo pezzo

                                        set<int> * blockSet = new set<int>();
                                        requestedBlocks.insert(pair<int, set<int> >(pieceNum,(*blockSet)));
                                        requestedBlocks[pieceNum].insert(j);

                                    }
                                    else if (requestedBlocks[pieceNum].find(j)==requestedBlocks[pieceNum].end()){
                                        // non avevo ancora richiesto questo blocco

                                        requestedBlocks[pieceNum].insert(j);

                                    }

                                    Request *requestMsg= new Request();
                                    requestMsg->setPieceIndex(pieceNum);
                                    requestMsg->setBlockIndex(j);
                                    numPeerReq++;

                                    EV << "Peer " << peerID << ": richiedo il blocco(" << (pieceNum) << ", " << (j) << ") al peer " << (o->first) << " \n";

                                    stringstream strStream;
                                    strStream << "Request (" << (pieceNum) << ", " << (j) << ")";
                                    o->second->myRequestedBlocks[pieceNum].insert(j);
                                    forwardMessage(requestMsg,o->first, strStream.str().c_str());

                                    if(numPeerReq>=maxReqLimit){
                                        stop=true;
                                        break;
                                    }
                                }

                        }
                }

            }
         }

        EV << "\n";
    }

    else if (!maliciousEnabled){  //richieste in base alla politica Random o Rarest First nel caso non malizioso

            if(requestedBlocks.find(pieceIndex)==requestedBlocks.end()){ // il numero di pezzo non era gia' inserito nella mappa

                set<int> * blockSet = new set<int>();
                requestedBlocks.insert(pair<int, set<int> >(pieceIndex,(*blockSet)));

            }


            if((myFile->getNumBlocksPerPiece()) < (par("maxBlockRequestNum").longValue()))
               EV << "ATTENZIONE! STAI USANDO UN MAX NUMERO DI BLOCCHI DA RICHIEDERE CHE E' MAGGIORE DEL NUMERO DI BLOCCHI PER PEZZO\n";


            int numPeerReq=0;

            for (map<int, set<int> >::iterator k =othersPeer[peer]->myRequestedBlocks.begin();k!=othersPeer[peer]->myRequestedBlocks.end();++k){

               numPeerReq=numPeerReq+k->second.size();

            }

            int j= 0;


            for(int i = 0; (i<myFile->getNumBlocksPerPiece()) && ((unsigned)j<(par("maxBlockRequestNum").longValue()-numPeerReq));++i){

                if(!(myFile->haveBlock(pieceIndex, i)) && (requestedBlocks[pieceIndex].find(i)==requestedBlocks[pieceIndex].end())){

                    // se non ho questo blocco e non l'ho ancora richiesto, allora lo devo richiedere

                    EV << "Peer " << peerID << ": richiedo il blocco(" << pieceIndex << ", " << i << ") al peer " << peer <<"\n";

                    Request * requestMsg=new Request();
                    requestMsg->setPieceIndex(pieceIndex);
                    requestMsg->setBlockIndex(i);
                    j++;

                    //salvo il blocco come gia' richiesto
                    requestedBlocks[pieceIndex].insert(i);


                    if( (!myFile->havePiece(pieceIndex)) && (othersPeer[peer]->myRequestedBlocks.find(pieceIndex) == othersPeer[peer]->myRequestedBlocks.end())){

                        // non avevo ancora richiesto nessun blocco di questo pezzo (che non ho) al peer

                        set<int> * blockSet = new set<int>();

                        othersPeer[peer]->myRequestedBlocks.insert(pair<int, set<int> >(pieceIndex,(*blockSet)));

                    }


                    othersPeer[peer]->myRequestedBlocks[pieceIndex].insert(i);

                    EV << "Peer " << peerID << ": Ho anche aggiunto questa richiesta in requestedBlocks, che adesso contiene:\n";

                    for (map<int, set<int> >::iterator k =requestedBlocks.begin();k!=requestedBlocks.end();++k){

                        EV << "+ (" << (k->first) << " , ";

                                    for (set<int>::iterator it = k->second.begin();it!=k->second.end();++it){

                                        EV << (*it) << " - ";

                                    }

                        EV << ")\n";

                    }


                    EV << "Peer " << peerID << ": Ho anche aggiunto questa richiesta in myRequestedBlocks associata al peer " << peer <<", che adesso contiene:\n";

                    for (map<int, set<int> >::iterator k =othersPeer[peer]->myRequestedBlocks.begin();k!=othersPeer[peer]->myRequestedBlocks.end();++k){

                        EV << "+ (" << (k->first) << " , ";

                                    for (set<int>::iterator it = k->second.begin();it!=k->second.end();++it){

                                        EV << (*it) << " - ";

                                    }

                        EV << ")\n";

                    }


                    stringstream strStream;
                    strStream << "Request(" << pieceIndex << ", " << i << ")";
                    forwardMessage(requestMsg,peer, strStream.str().c_str());

                }

            }

    }


   // se ho richiesto tutti i blocchi di questo pezzo, allora lo rimuovo dalla coda piecesQueue
   int nb= myFile->getNumBlocksPerPiece();
   bool remove= true;
   if (piecesQueue.empty()) {

       bool allBlockReq=true;

       if (pieceIndex!=-5) {
           for (int k=0; k<nb; k++) {

                if ( (!myFile->haveBlock(pieceIndex, k)) && (requestedBlocks[pieceIndex].find(k) == requestedBlocks[pieceIndex].end()) ) {
                    allBlockReq= false;
                    break;
                }

            }

       } else {
           // sono malizioso e ho gia' richiesto tutti i blocchi in requestedBlocks
          allBlockReq= true; // ma lo era gia'...
       }

       if(allBlockReq){
           lastPieceSel=-1;
       }


       remove= false;
   /*} else if (piecesQueue.back()->index != pieceIndex) {
       remove= false;*/
   } else {
       for (int k=0; k<nb; k++) {
           // per ogni blocco, se (possiedo gia' questo blocco OR ho richiesto questo blocco) allora rimuovi il pezzo
           // se esiste almeno un blocco per cui NOT (possiedo gia' questo blocco OR ho richiesto questo blocco) allora non rimuovo il pezzo

           // NOT (possiedo gia' questo blocco OR ho richiesto questo blocco)
           // NB: ho applicato de morgan
           if ( (!myFile->haveBlock(pieceIndex, k)) && (requestedBlocks[pieceIndex].find(k) == requestedBlocks[pieceIndex].end()) ) {
               remove= false;
               break;
           }
       }
   }

   if (remove) {
       /*PieceScore * frontPiece;
       frontPiece= piecesQueue.back();
       int front= frontPiece->index;
       piecesQueue.pop_back();
       delete frontPiece;*/

       bool removed=false;
       PieceScore * removedPiece;
       int index;

       for (vector<PieceScore*>::iterator it = piecesQueue.begin(); it != piecesQueue.end(); ++it){

           removedPiece=*it;
           index=(*it)->index;
           if(index==pieceIndex){
               piecesQueue.erase(it);
               removed=true;
               lastPieceSel= -1;
               break;
           }


       }

       if(removed){
           delete removedPiece;
           EV << "Peer " << peerID << ": ho richiesto tutti i blocchi del pezzo " << pieceIndex << "\n";
           EV << "Peer " << peerID << ", requestPiece(): rimosso " << index << " dalla coda -> [ ";
           for (vector<PieceScore*>::reverse_iterator p = piecesQueue.rbegin(); p != piecesQueue.rend(); ++p){
               EV << "(i " << ((*p)->index) << ", s " << ((*p)->score) << "), ";
           }
           EV << " ]\n";
       }
   }

   if (!maliciousEnabled && selectPiece()==-1) { // ho gia' richiesto tutti i blocchi del file che mi mancano e sono entrato nella politica END_GAME
       requestPiece(-1, -1);
   }


   EV << "\n\n";

}

void Peer::handleHave(Have *haveMsg){

    // un peer mi ha comunicato di essere in possesso di un pezzo

    int peer = haveMsg->getSource(); //recupero il peerID del peer remoto

    int piece = haveMsg->getPieceIndex(); // recupero l'indice del pezzo in possesso del peer remoto


    EV << "Peer " << peerID << ": il peer " << peer << " mi ha inviato Have per il pezzo " << piece << "\n";

    //aggiorno la mia mappa locale

    RemotePeer * peerInfo = othersPeer[peer];

    bool iKnow=false;

    if(!(peerInfo->btFile->havePiece(piece))){
        peerInfo->btFile->setPiece(piece, true); //segno tutti i blocchi del pezzo come in possesso del peer
    }
    else{

        EV << "Peer " << peerID << ": sapevo gia' che " << peer << " possedeva " << piece << " (NON DOVREBBE ACCADERE) \n";
        iKnow=true;
    }

    // aggiorna i peer a cui sono interessato (solo se non sono un seeder e non ero a conoscenza che il peer aveva questo pezzo)
    if(!myFile->haveTorrent() && (!iKnow))
        updateInterests(-1);

    if(myFile->haveTorrent() && (peerInfo->btFile->haveTorrent())){ // se sono un seeder e il peer che mi ha inviato Have e' diventato seeder lo rimuovo dai peer conosciuti


		  RemotePeer * rPeer= peerInfo;
          cancelAndDelete(rPeer->timeout);
          connection_lost(peer); //la rimozione del peer da othersPeer viene gia' fatta in connection_lost

          return;

    }


    if(!maliciousEnabled && policy==END_GAME && (!iKnow) && (!myFile->havePiece(piece)) && (othersPeer[peer]->am_interested) && (!(othersPeer[peer]->peer_choking))){
        // sono nella politica END_GAME e non ho ancora ottenuto questo pezzo

        // richiedo al peer i blocchi che mi mancano di questo pezzo se non mi sta soffocando


        if( (othersPeer[peer]->myRequestedBlocks.find(piece) == othersPeer[peer]->myRequestedBlocks.end())){

            // non avevo ancora richiesto nessun blocco di questo pezzo (che non ho) al peer

            set<int> * blockSet = new set<int>();

            othersPeer[peer]->myRequestedBlocks.insert(pair<int, set<int> >(piece,(*blockSet)));

        }


        for (int k = 0; k < myFile->getNumBlocksPerPiece(); ++k) {

            // itero i blocchi
            

           if ( (!(myFile->haveBlock(piece,k))) &&
                   (othersPeer[peer]->myRequestedBlocks[piece].find(k) == othersPeer[peer]->myRequestedBlocks[piece].end())
               ) {
               // non ho questo blocco e non ho ancora richiesto questo blocco a quel peer (che ha il blocco)

               if(requestedBlocks.find(piece)==requestedBlocks.end()){
                   // non avevo ancora richiesto questo pezzo

                   set<int> * blockSet = new set<int>();
                   requestedBlocks.insert(pair<int, set<int> >(piece,(*blockSet)));
                   requestedBlocks[piece].insert(k);

               }
               else if (requestedBlocks[piece].find(k)==requestedBlocks[piece].end()){
                   // non avevo ancora richiesto questo blocco

                   requestedBlocks[piece].insert(k);

               }
            

                Request *requestMsg= new Request();
                requestMsg->setPieceIndex(piece);
                requestMsg->setBlockIndex(k);

                EV << "Peer " << peerID << ": richiedo il blocco(" << (piece) << ", " << (k) << ") anche al peer " << (peer) << " \n";

                stringstream strStream;
                strStream << "Request (" << (piece) << ", " << (k) << ")";
                othersPeer[peer]->myRequestedBlocks[piece].insert(k);
                forwardMessage(requestMsg,peer, strStream.str().c_str());

            }
        }

    }
/* TODO ripristinare alpha
    else if((!(othersPeer[peer]->peer_choking)) && (othersPeer[peer]->am_interested) &&
            (!iKnow) && (!myFile->havePiece(piece)) && (!(policy==END_GAME))
            && (othersPeer[peer]->myRequestedBlocks.size()==0)){
        //se sto usando il protocollo normale o l'end game malizioso e
        // se il peer non mi sta soffocando e non ho questo pezzo e non gli ho fatto ancora alcuna richiesta
        // gli chiedo il pezzo

        requestPiece(piece,peer);


    }
*/
    EV <<"\n";

}


// controlla i peer ai quali sono interessato
int Peer::updateInterests(int peer){
    bool seeder= myFile->haveTorrent();

    if(!seeder) {
        // se non sono un seeder, aggiorno la coda delle richieste
        updateQueue();
    }
	
    int interestingPiece = selectPiece();
    
    int returnPiece=interestingPiece;

    string ply[] = {"RAREST_FIRST","RANDOM_FIRST_PIECE","END_GAME"};

    EV << "Peer " << peerID << ": la policy " << ply[policy] << " mi impone di richiedere blocchi del pezzo di indice " << interestingPiece << "\n";


   if(maliciousEnabled && interestingPiece!=-1 && interestingPiece!=-5){ // indice di pezzo valido

       //end_game malizioso

       for (map<int, RemotePeer* >::iterator i = othersPeer.begin(); i != othersPeer.end(); ++i){

                  // itero i peer remoti

                  bool interested=false;

                  if(!seeder){

                              if((i->second->btFile->havePiece(interestingPiece)) && (!(myFile->havePiece(interestingPiece)))){

                                  // il peer possiede il pezzo restituito dalla policy che io no ho
                                      interested=true;

                              }
                  }


                  if(interested || (i->second->myRequestedBlocks.size()!=0 && i->second->am_interested && !seeder)){

                          //sono interessato al peer remoto perche' possiede il pezzo indicato dalla policy
                          // oppure perche' ho ancora richieste pendenti

                          EV << "Peer "<< peerID << ": sono interessato al peer: " << (i->first) << "\n";

                          if(!interested && (i->first==peer)){

                              returnPiece=-2;
                          }


                          if(!(i->second->am_interested)){

                              EV << "Gli mando Interested\n";

                              i->second->am_interested=true;

                              Interested * intMsg = new Interested();

                              forwardMessage(intMsg,i->first,"Interested");


                          }
                  }

                  else{

                      /* invio messaggio NotInterested al peer remoto se non l'ho gia' fatto */

                          EV << "Peer " << peerID << ": non sono interessato al peer: " << (i->first) << "\n";


                          if(i->second->am_interested){

                              EV<<"Gli mando NotInterested\n";

                              i->second->am_interested=false;

                              NotInterested * notIntMsg = new NotInterested();

                              forwardMessage(notIntMsg,i->first,"NotInterested");


                          }

                  }

       }
   }

   else  if ( ( (interestingPiece==-1) && (!seeder) && !maliciousEnabled ) || (maliciousEnabled && interestingPiece==-5) ) {
        // sono nella politica end game (non maliziosa) e non sono un seeder
       // oppure sono malizioso e la coda piecesQueue e' vuota

        for (map<int, RemotePeer* >::iterator i = othersPeer.begin(); i != othersPeer.end(); ++i){

            // itero i peer remoti

            bool interested=false;


            if(!seeder){
                        for (int j=0; j < myFile->getNumPieces(); ++j) {

                            if((i->second->btFile->havePiece(j)) && (!(myFile->havePiece(j)))){

                                // il peer possiede un pezzo che io no ho

                                //invio messaggio Interested al peer remoto (se non l'ho gia' fatto)

                                    interested=true;
                                    break;

                            }


                        }
            }


                if(interested){
                        EV << "Peer "<< peerID << ": sono interessato al peer: " << (i->first) << "\n";

                        if(!(i->second->am_interested)){

                            EV << "Gli mando Interested\n";

                            i->second->am_interested=true;

                            Interested * intMsg = new Interested();

                            forwardMessage(intMsg,i->first,"Interested");


                        }
                }

                else{

                    /* invio messaggio NotInterested al peer remoto se non l'ho gia' fatto */

                        EV << "Peer " << peerID << ": non sono interessato al peer: " << (i->first) << "\n";


                        if(i->second->am_interested){

                            EV<<"Gli mando NotInterested\n";

                            i->second->am_interested=false;

                            NotInterested * notIntMsg = new NotInterested();

                            forwardMessage(notIntMsg,i->first,"NotInterested");


                        }

                }

        }


    } else if (!maliciousEnabled){
        // NB: (non sono nella politica end game) OR (sono un seeder)
        // se sono un seeder => invio NotInterested a tutti quelli a cui ero interessato
        // se non sono nella politica end game => calcolo peerHave e iHave e agisco di conseguenza

        if(!seeder){

            EV << "Peer " << peerID << ": sono interessato al pezzo " << interestingPiece << "\n";

        }
        else {

            EV << "Peer " << peerID << " (sono seeder): sono interessato al pezzo " << interestingPiece << " (DEVE ESSERE -1)\n";

        }
        EV << "Peer " << peerID << ": piecesQueue -> [ ";
        for (vector<PieceScore*>::reverse_iterator p = piecesQueue.rbegin(); p != piecesQueue.rend(); ++p){
            EV << "(i " << ((*p)->index) << ", s " << ((*p)->score) << "), ";
        }
        EV << " ]\n";

        for (map<int, RemotePeer *>::iterator it = othersPeer.begin(); it != othersPeer.end(); ++it){

            bool interested;

            if (seeder) {
                // sono un seeder
                interested= false;

            } else {
                bool peerHave = it->second->btFile->havePiece(interestingPiece); //pezzo dal punto di vista del peer remoto

                bool iHave = myFile->havePiece(interestingPiece);

                interested= ((peerHave) && (!iHave));
            }

            if(interested){

                //invio messaggio Interested al peer remoto (se non l'ho gia' fatto)

                EV << "Peer "<< peerID << ": sono interessato al peer: " << (it->first) << "\n";

                if(!(it->second->am_interested)){

                    EV << "Gli mando Interested\n";

                    it->second->am_interested=true;

                    Interested * intMsg = new Interested();

                    forwardMessage(intMsg,it->first,"Interested");


                }

            } else{


                bool notUseful=false;


                if(it->second->am_interested && (!(othersPeer[it->first]->peer_choking)) && !seeder){

                    //se secondo la politica che sto adottando non dovrei piu' interessarmi a questo peer ma
                    // se esso mi sta mantenendo unchoked provo a vedere se ha qualche altro pezzo che mi puo' tornare utile

                    int usefulPiece=-1;


                    for(int j=0;j< myFile->getNumPieces();j++){

                        bool peerHave = othersPeer[it->first]->btFile->havePiece(j);

                        bool iHave = myFile->havePiece(j);

                        if(peerHave && !iHave){
                            usefulPiece=j;
                            break;
                        }

                    }

                    if(usefulPiece!=-1){
                        EV << "Peer " << peerID << ": resto interessato al peer: " << (it->first) << " "
                                "con un pezzo diverso da quello suggerito dalla politica\n";

                        if(it->first==peer)
                            returnPiece=usefulPiece;
                    }
                    else{

                        notUseful=true;
                    }

                }

                /* invio messaggio NotInterested al peer remoto se non l'ho gia' fatto e
                 * se non ha in coda blocchi che gli ho richiesto */

                if((it->second->am_interested && (it->second->myRequestedBlocks.size()==0) && notUseful) ||
                        ((it->second->am_interested && (it->second->myRequestedBlocks.size()==0) && seeder))){

                    EV << "Peer " << peerID << ": non sono interessato al peer: " << (it->first) << "\n";

                    EV<<"Gli mando NotInterested\n";

                    it->second->am_interested=false;

                    NotInterested * notIntMsg = new NotInterested();

                    forwardMessage(notIntMsg,it->first,"NotInterested");


                }

                else if(it->second->am_interested && (it->second->myRequestedBlocks.size()!=0) && notUseful){

                    assert(!seeder); //nessuno deve ancora inviarmi dei blocchi: sono seeder

                    EV << "Peer " << peerID << ": il peer: " << (it->first) << " non ha piu' pezzi che mi interessano ma deve ancora inviarmi"
                            " dei blocchi che ho richiesto. Lo mantengo Interested\n";

                    if(it->first==peer)
                        returnPiece = -2; // restituisco -2 se voglio rimanere interessato ad un peer che non mi ha ancora finito di inviare dei blocchi che gli ho richiesto

                }

            }

        }

        EV << "\n";

    }

    EV << "Peer " << peerID << ": updateInterests ritorna pezzo --> " << interestingPiece << "\n";


    return returnPiece;

}




void Peer::handleInterested(Interested *interestedMsg){

    /* un peer e' interessato ad un pezzo in mio possesso
      ci comunica che il peer remoto iniziera' a richiedere i blocchi quando il client ne effettua l'unchoke */

    int peer=interestedMsg->getSource();


    EV << "Peer " << peerID << ": il peer " << peer << " mi ha inviato Interested\n";

    othersPeer[peer]->peer_interested=true;

    if(othersPeer[peer]->pending_unchoking || ((chokingMsg==NULL) && active)){
        // se e' uno dei peer pending_unchoked che e' diventato interested oppure se non e' stato schedulato un messaggio ChokingAlgorithm e io sono attivo
        // rieseguo l'algoritmo di choking

        if(othersPeer[peer]->pending_unchoking)
            EV << "Peer " << peerID << ": " << peer << " era pending_unchoked quindi eseguo l'algoritmo di choking\n";
        else if(chokingMsg==NULL)
            EV << "Peer " << peerID << ": " << peer << " e' interested, riavvio l'algoritmo di choking\n";

        //chokingAlgorithm(0);

        if(chokingMsg==NULL){
        
#ifndef NO_COUT
            cout << "Peer " << peerID << ": Interested da " << peer << " riattiva choking\n";
            cout.flush();
#endif
            round_robin();
        }
        else{
            rechoke();
        }
    }

}

void Peer::handleNotInterested(NotInterested *notInterestedMsg){

    // un peer non e' interessato ai pezzi in mio possesso

    int peer=notInterestedMsg->getSource();

    EV << "Peer " << peerID << ": il peer " << peer << " mi ha inviato NotInterested\n";

    othersPeer[peer]->peer_interested=false;

    if(!(othersPeer[peer]->am_choking)){
        // se e' uno dei peer unchocked, rieseguo l'algoritmo di chocking
        EV << "Peer " << peerID << ": era unchoked quindi eseguo l'algoritmo di choking\n";
         //chokingAlgorithm(0);
        rechoke();
    }

}

void Peer::handleKeepAlive(KeepAlive *msg){
    // serve per mantenere viva la connessione con un peer

    /* se questo messaggio riguardante quel peer non viene ricevuto entro 2 minuti (tempo simulato)
        la connessione verso quel peer viene chiusa (se nel frattempo non sono stati ricevuti altri messaggi da parte di quel peer) */

    // nulla da fare! vedi handleBitTorrentMessage()

}

void Peer::handlePiece(Piece *pieceMsg){

    // un peer mi ha inviato un blocco

    numPieceForMe++;
    emit(numPieceForMeSignal, numPieceForMe);

    int pieceIndex=pieceMsg->getPieceIndex();

    int blockIndex=pieceMsg->getBlockIndex();

    int peer = pieceMsg->getSource();

    EV << "Peer " << peerID << ": il peer " << peer << " mi ha inviato il blocco (" << pieceIndex << ", " << blockIndex << ")\n";


    bool requested = ((requestedBlocks.find(pieceIndex)!=requestedBlocks.end()) && (requestedBlocks[pieceIndex].find(blockIndex)!=requestedBlocks[pieceIndex].end()));

    if(!(myFile->haveBlock(pieceIndex,blockIndex)) && requested){//se non avevo ancora questo blocco e ho richiesto il blocco


        // setto nel mio file bitTorrent il blocco come posseduto
        myFile->setBlock(pieceIndex,blockIndex);

        // aggiorno l'upload rate del peer remoto che mi ha inviato il blocco
        (othersPeer[peer]->uploadRate.back())++;

        EV << "Peer " << peerID << ": ho incrementato l'upload rate del peer " << peer << " (upload rate = " << (othersPeer[peer]->uploadRate.back()) << ")\n";


        //rimuovo il blocco dalle richieste fatte
        othersPeer[peer]->myRequestedBlocks[pieceIndex].erase(blockIndex);

        if((othersPeer[peer]->myRequestedBlocks[pieceIndex].size())==0) // non ho richiesto al peer remoto altri blocchi di quel pezzo
            othersPeer[peer]->myRequestedBlocks.erase(pieceIndex);

        requestedBlocks[pieceIndex].erase(blockIndex);


        EV << "Peer " << peerID << ": Ho rimosso richiesta blocco da requestedBlocks, che adesso contiene:\n";


        for (map<int, set<int> >::iterator k =requestedBlocks.begin();k!=requestedBlocks.end();++k){

        EV << "+ (" << (k->first) << " , ";

                    for (set<int>::iterator it = k->second.begin();it!=k->second.end();++it){

                        EV << (*it) << " - ";

                    }

        EV << ")\n";

        }


        EV << "Peer " << peerID << ": Ho rimosso richiesta blocco da myRequestedBlocks associata al peer "<< peer <<", che adesso contiene:\n";

        for (map<int, set<int> >::iterator k =othersPeer[peer]->myRequestedBlocks.begin();k!=othersPeer[peer]->myRequestedBlocks.end();++k){

            EV << "+ (" << (k->first) << " , ";

                        for (set<int>::iterator it = k->second.begin();it!=k->second.end();++it){

                            EV << (*it) << " - ";

                        }

            EV << ")\n";

        }


        /* se stavo usando la politica di End Game significa che questo blocco
         * probabilmente l'avevo gia' richiesto anche agli altri peer quindi devo mandare a questi
         * un messaggio di Cancel se loro non mi hanno inviato choked */


        if(policy==END_GAME || maliciousEnabled){

            EV << "sono nella politica di END_GAME quindi invio Cancel agli altri peer a cui avevo richiesto il pezzo se non mi stanno soffocando\n";

            vector<int> excludedDests;

            excludedDests.push_back(peer);

            for (map<int, RemotePeer *>::iterator i = othersPeer.begin(); i != othersPeer.end(); ++i){

                if(((i->second->myRequestedBlocks.find(pieceIndex))!=(i->second->myRequestedBlocks.end())) &&
                        ((i->second->myRequestedBlocks[pieceIndex].find(blockIndex))!=(i->second->myRequestedBlocks[pieceIndex].end()))){
                    // ho richiesto al peer il blocco che ho appena ottenuto da un altro peer

                    i->second->myRequestedBlocks[pieceIndex].erase(blockIndex); // rimuovo il blocco dalla mappa myRequestedBlocks associata al peer

                    if((i->second->myRequestedBlocks[pieceIndex].size())==0) // non ho richiesto al peer remoto altri blocchi di quel pezzo
                        i->second->myRequestedBlocks.erase(pieceIndex);      // quindi rimuovo anche l'indice del pezzo dalla mappa myRequestedBlocks

                    // se il peer mi sta soffocando, non gli invio il Cancel (ci avra' gia' pensato lui a rimuovere la mia richiesta quando mi ha soffocato)
                    if(othersPeer[i->first]->peer_choking)
                       excludedDests.push_back(i->first);
                    else
                       (othersPeer[i->first]->canceledRate.back())++;

                }

                else{ //non ho mai richiesto al peer il blocco quindi non devo inviargli il Cancel

                    excludedDests.push_back(i->first);

                }

            }


            Cancel *cancelMsg= new Cancel();
            cancelMsg->setPieceIndex(pieceIndex);
            cancelMsg->setBlockIndex(blockIndex);

            stringstream strStream;
            strStream << "Cancel(" << pieceIndex << ", " << blockIndex << ")";

            multicastMessage(cancelMsg,excludedDests,strStream.str().c_str());
            delete cancelMsg; // rimuovo perche' ne viene inviata una copia

        }


        // controllo se con questo blocco ho terminato il pezzo

        if(myFile->havePiece(pieceIndex)){


            EV << "Peer " << peerID << ": con questo blocco " << blockIndex << " ho terminato il pezzo " << pieceIndex << ".\n";


            for (map<int, RemotePeer *>::iterator i = othersPeer.begin(); i != othersPeer.end();++i){


                    if(othersPeer[i->first]->myRequestedBlocks.find(pieceIndex)!=othersPeer[i->first]->myRequestedBlocks.end()){
                        othersPeer[i->first]->myRequestedBlocks.erase(pieceIndex);
                    }

            }


            requestedBlocks.erase(pieceIndex);

            EV << "Peer " << peerID << ": Ho rimosso la richiesta del pezzo da requestedBlocks, che adesso contiene:\n";


            for (map<int, set<int> >::iterator k =requestedBlocks.begin();k!=requestedBlocks.end();++k){

            EV << "+ (" << (k->first) << " , ";

                        for (set<int>::iterator it = k->second.begin();it!=k->second.end();++it){

                            EV << (*it) << " - ";

                        }

            EV << ")\n";

            }


			EV << "Peer " << peerID << ": Ho rimosso la richiesta del pezzo da myRequestedBlocks associata al peer "<< peer <<", che adesso contiene:\n";

            for (map<int, set<int> >::iterator k =othersPeer[peer]->myRequestedBlocks.begin();k!=othersPeer[peer]->myRequestedBlocks.end();++k){

                EV << "+ (" << (k->first) << " , ";

                            for (set<int>::iterator it = k->second.begin();it!=k->second.end();++it){

                                EV << (*it) << " - ";

                            }

                EV << ")\n";

            }


            //se ho ottenuto un pezzo invio il relativo messaggio Have agli altri peer
            EV << "Peer " << peerID << ": Invio Have con il pezzo appena ottenuto a tutti gli altri peer\n";

            Have *haveMsg= new Have();
            haveMsg->setPieceIndex(pieceIndex);

            stringstream strStream;
            strStream << "Have(" << pieceIndex  << ")";

            multicastMessage(haveMsg,strStream.str().c_str());
            delete haveMsg; // rimuovo perche' ne viene inviata una copia

            if(myFile->haveTorrent()){ //sono diventato seeder

                //cout << peerID << " --> sono SEEDER\n";
                //cout.flush();

                seederTime= simTime();
                emit(seederTimeSignal, seederTime-joinTime);

                emit(newSeederSignal, BTStatistics::addNewSeeder(peerID));
                tracker->trackerRequest(peerID,getRemainingPieces(),"completed",0);
                // e' inutile continuare a fare regular requests al tracker
                cancelAndDelete(trackerRequest);
                trackerRequest=NULL;
                // rimuovo dai peer che conosco gli altri peer che sono seeder

                updateInterests(-1);

                bool conn_lost=false;

                for (map<int, RemotePeer *>::iterator i = othersPeer.begin(); i != othersPeer.end();){
                    if(i->second->btFile->haveTorrent()){ // il peer remoto e' seeder
                        // lo rimuovo

                       connections.remove(i->first);

                       if(othersPeer[i->first]->peer_interested && (!(othersPeer[i->first]->am_choking)))
                           conn_lost=true;

                       RemotePeer * rPeer= i->second;

                       cancelAndDelete(rPeer->timeout);
                       rPeer->timeout= NULL;

                       delete rPeer;
                       othersPeer.erase(i++); // post-incremento i per non invalidarlo

                    }
                    else {

                        //cout << "Secondo me il peer " << i->first << " non e' SEEDER\n";
                        //                        cout.flush();
                        ++i;
                    }

                }

                if(conn_lost)
                    connection_lost(-1);


                emit(newDownloaderSignal, BTStatistics::delDownloader(peerID));
                getDisplayString().setTagArg("i", 1, "gold");
            }


        }

        if((othersPeer.find(peer) != othersPeer.end()) && (!myFile->haveTorrent())){

            // aggiorna i peer a cui sono interessato, se sono in END_GAME si limitera', se necessario, ad inviare un NotInterested al peer
            int piece = updateInterests(peer);

            if(piece!=-1){ // non sono nella politica END_GAME normale, potrei pero' entrare con l'end game malizioso

                if(othersPeer[peer]->am_interested && (!(othersPeer[peer]->peer_choking))){

                    EV << "Peer " << peerID << ": sono ancora interessato al peer " << peer << "; gli richiedo altri blocchi\n";

                    if(piece!=-2){
                        //gli faccio una nuova richiesta
                        requestPiece(piece,peer);
                    }

                }
            }
        }




    }

    else{

        duplicatedBlocks++;
        emit(duplicatedBlocksSignal, duplicatedBlocks);
        EV << "Peer " << peerID << ": non mi interessa il blocco che mi e' arrivato\n";

    }

    char buf[10];
    sprintf(buf, "%.2f%%", (myFile->getCompletation()*100));
    getDisplayString().setTagArg("t", 0, buf);
    getDisplayString().setTagArg("tt", 0, buf);
    bubble(">b");
    emit(fileCompletationSignal, myFile->getCompletation());


    EV << "Pezzi di " << peerID << ": " << (myFile->toString()) << "\n\n";


}

void Peer::handleRequest(Request *requestMsg){

    // un peer mi ha richiesto un blocco

    numRequestForMe++;
    emit(numRequestForMeSignal, numRequestForMe);

    int peer = requestMsg->getSource();


    EV << "Peer " << peerID << ": il peer " << peer <<" mi ha richiesto il blocco (" << (requestMsg->getPieceIndex()) << ", " << (requestMsg->getBlockIndex()) << ")\n";


    if(othersPeer[requestMsg->getSource()]->am_choking==false){ //non ho soffocato il peer

        //il peer remoto mi deve richiedere un blocco che possiedo
        assert(myFile->haveBlock(requestMsg->getPieceIndex(),requestMsg->getBlockIndex()));

        // lo copio per evitare che poi mediante il delete di questo metodo venga rimosso
       //TODO ricordarsi poi di eliminare anche questi messaggi una volta estratti dalla coda
       Request *copy = (Request *) requestMsg->dup();
       othersPeer[peer]->requestsQueue.push_back(copy);

       EV << "Peer "<< peerID << ": la lista di richieste riguardanti il peer " << peer << " che ho salvato nella relativa coda requestsQueue e' cosi' composta:\n";
       for(list<Request*>::iterator it = othersPeer[peer]->requestsQueue.begin();it!=othersPeer[peer]->requestsQueue.end();++it){

           EV << "(" << ((*it)->getPieceIndex()) << ", " << ((*it)->getBlockIndex()) << ")\n";

       }

       EV << "\n";


       Piece * pieceMsg = new Piece();
       blocksSent++;
       emit(blocksSentSignal, (float)blocksSent/myFile->getTotBlocks());
       bubble("<<<out<<<blk<<<");

       pieceMsg->setPieceIndex(copy->getPieceIndex());
       pieceMsg->setBlockIndex(copy->getBlockIndex());

       stringstream strStream;
       strStream << "Piece (" << (pieceMsg->getPieceIndex()) << ", " << (pieceMsg->getBlockIndex()) << ")";
       forwardMessage(pieceMsg,peer, strStream.str().c_str(), simulation.getSystemModule()->par("blockLength").longValue());


    } else{

    //else se il peer e' stato soffocato non rispondo

        EV << "Peer " << peerID << ": il peer" << peer << " risulta soffocato (non rispondo alla richiesta)\n";

    }

    EV << "\n";


}

// // //
// MESSAGGI INTERNI

void Peer::handleInternalMessage(InternalMsg *imsg) {

    if(JoinSwarm* joinMsg = dynamic_cast<JoinSwarm*>(imsg)) {

        handleJoinSwarm(joinMsg);

    } else if(LeaveSwarm* leaveMsg = dynamic_cast<LeaveSwarm*>(imsg)) {

        handleLeaveSwarm(leaveMsg);

    } else if(UpdateRate* updateRateMsg = dynamic_cast<UpdateRate*>(imsg)) {

        if (active) {
            handleUpdateRate(updateRateMsg);
        }

    } else if(ChokingAlgorithm* chokingAlgMsg = dynamic_cast<ChokingAlgorithm*>(imsg)) {

        if (active) {
            handleChokingAlgorithm(chokingAlgMsg);
        }

	} else if(ExtractIncomingMsg* excInMsg = dynamic_cast<ExtractIncomingMsg*>(imsg)) {

	    if (active) {
	        handleExtractIncomingMsg(excInMsg);
	    }

    } else if(ExtractOutgoingMsg* excOutMsg = dynamic_cast<ExtractOutgoingMsg*>(imsg)) {

        if (active) {
            handleExtractOutgoingMsg(excOutMsg);
        }

    } else if (PeerTimeout* timeoutMsg = dynamic_cast<PeerTimeout*>(imsg)) {

        if (active) {
        	handlePeerTimeout(timeoutMsg);
        }

    }  else if (StartKeepAlive* startKeepAliveMsg = dynamic_cast<StartKeepAlive*>(imsg)) {

        if (active) {

            handleStartKeepAlive(startKeepAliveMsg);

        }

    } else if (TrackerRegularRequest* trackerRequestMsg = dynamic_cast<TrackerRegularRequest*>(imsg)) {

        if (active) {

            handleTrackerRequest(trackerRequestMsg);

        }


    } else if (DataStream* dataStreamMsg = dynamic_cast<DataStream*>(imsg)) {

        if (active) {

            handleDataStream(dataStreamMsg);

        }


    }

    // in ogni caso cancello il messaggio
    delete imsg;

}

void Peer::handleDataStream(DataStream * msg) {

    if (msg->getStart()) {
        // inizio dello stream

        if (myFile->haveTorrent()) {
            getDisplayString().setTagArg("i", 1, "#994d00");
        } else {
            getDisplayString().setTagArg("i", 1, "#004c00");
        }
        getDisplayString().setTagArg("i", 2, "100");
        EV << "Inizio data-stream" << endl;

    } else {

        // fine dello stream
        if (myFile->haveTorrent()) {
            getDisplayString().setTagArg("i", 1, "gold");
        } else {
            getDisplayString().setTagArg("i", 1, "green");
        }
        getDisplayString().setTagArg("i", 2, "30");
        EV << "Fine data-stream" << endl;
    }
}

void Peer::handleJoinSwarm(JoinSwarm *joinMsg) {

    assert(active==false);

    cModule *sysM= simulation.getSystemModule();
    long numPieces= sysM->par("numPieces").longValue();

    // entro nello swarm
    active= true;
    blocksSent= 0;
    emit(blocksSentSignal, (float)blocksSent);
    duplicatedBlocks= 0;
    emit(duplicatedBlocksSignal, duplicatedBlocks);
    effectiveCancels= 0;
    emit(effectiveCancelsSignal, effectiveCancels);
    numRequestSent=0;
    numPieceSent=0;
    numCancelSent=0;
    numCancelForMe=0;
    numRequestForMe=0;
    numPieceForMe=0;


    bool bitfield[numPieces];
    if (joinMsg->getEmpty() || !joinMsg->getAgainRandom()) {
        // inizio da zero o inizializzo a random
        // in entrambi i casi creo un nuovo torrent
        myFile= new BitTorrentFile(numPieces);

        // inizializzo il mio file
        double pInit; // probabilita' per l'inizializzazione del torrent file
        if (joinMsg->getEmpty()) {
            pInit= 0.0; // vuoto
        } else if (!joinMsg->getAgainRandom()) {
            pInit = par("piecesInit").doubleValue(); // random
        } // else stesso torrent: nulla da fare
        if (tracker->mustBeSeeder(peerID)) {
            pInit= 1.0; // questo e' il seeder
        }
        myFile->initTorrentFile(bitfield, pInit); // inizializzazione
    } else {
        // riprendo lo stesso torrent
        for (int k=0; k<numPieces; k++) {
            bitfield[k]= myFile->havePiece(k);
        }
    }

    EV << "Pezzi di " << peerID << ": " << (myFile->toString()) << "\n";

    joinTime= simTime();
    if (myFile->haveTorrent()) {
        getDisplayString().setTagArg("i", 1, "gold");
        emit(newSeederSignal, BTStatistics::addNewSeeder(peerID));

        seederTime= simTime();
        emit(seederTimeSignal, seederTime-joinTime);

    } else {
        getDisplayString().setTagArg("i", 1, "green");
        emit(newDownloaderSignal, BTStatistics::addNewDownloader(peerID));
    }

    char buf[10];
    sprintf(buf, "%.2f%%", (myFile->getCompletation()*100));
    //bubble(buf);
    getDisplayString().setTagArg("t", 0, buf);
    getDisplayString().setTagArg("tt", 0, buf);
    emit(fileCompletationSignal, myFile->getCompletation());

    if(!myFile->haveTorrent()){
        policy= RANDOM_FIRST_PIECE;
        initQueue();
    } //else se sono un seeder non mi interessa la politica di scelta dei pezzi


    chokingMsg=new ChokingAlgorithm("ChokingAlgorithm");
    //chokingMsg->setSec(10);

    updateRate = new UpdateRate("UpdateRate");

    // mi invio i messaggi per l'algoritmo di choking/unchoking
    scheduleAt(simTime()+simTime().parse("10s"), chokingMsg);
    scheduleAt(simTime()+simTime().parse("10s"), updateRate);

    startKeepAlive = new StartKeepAlive("StartKeepAlive");

    if (!par("disableKeepAlive").boolValue()) {
        //avvio il timer per tenere attive le connessioni con gli altri peer
        scheduleAt(simTime() + SimTime::parse("60s") , startKeepAlive);
    }

    // schedulo quando usciro'
    simtime_t delta= par("leaveDelay");
    if (delta>0) {
        scheduleAt(simTime()+delta, new LeaveSwarm("LeaveSwarm"));
    } // else (delta==0) => non lascio mai lo swarm

    set<int> * pSet = tracker->trackerRequest(peerID, getRemainingPieces(), "started", par("maxPeerSet").longValue());

    // se sono un seeder non mi interessa fare l'handshake agli altri peer, saranno loro che lo faranno a me

    if(!(myFile->haveTorrent())){ // non sono un seeder

        trackerRequest = new TrackerRegularRequest("TrackerRegularRequest");

        if(((unsigned) (pSet->size())) < par("minPeerSet").longValue()){ // mi sono stati restituiti pochi peer
            scheduleAt(simTime()+simTime().parse("600s"), trackerRequest);
        }
        else{
            scheduleAt(simTime()+simTime().parse("1800s"), trackerRequest);
        }

        // invio in broadcast
        if((pSet!=NULL) && (pSet->size()!=0)){
            EV << "Peer:" << peerID << ": Invio l'handshake ai seguenti peer: \n";
            for (set<int>::iterator it= pSet->begin(); it != pSet->end(); ++it) {
                    EV << *it << ", ";
            }
            EV << endl;

            // popolo la mappa othersPeer
            for (set<int>::iterator remotePeer= pSet->begin(); remotePeer != pSet->end(); ++remotePeer) {
                //aggiorno la mia mappa locale con il bitfield e l'ID del peer

                RemotePeer * peerInfo = new RemotePeer();

                //inizializzo i campi associati al peer remoto

                peerInfo->btFile= new BitTorrentFile(myFile->getNumPieces()); // inizializzato di default come vuoto

               /*
                * le connessioni con un client iniziano come choked e not interested.
                * Un blocco e' scaricato dal client quando il client e' interessato ad un peer e quel peer non sta soffocando il client
                * Un blocco e' inviato da un client quando il client non sta soffocando il peer e quel peer e' interessato al client.
                * E' importante per il client tenere i suoi peer informati riguardo il suo interesse o meno verso di loro.
                * Questa informazione dovrebbe essere aggiornata anche dai peer che hanno soffocato il client che la invia in modo che essi sono a conoscenza se quel client
                * iniziera' il download quando viene unchoked (e viceversa).
                */

                peerInfo->am_choking=1;
                peerInfo->am_interested=0;
                peerInfo->pending_unchoking=0;
                peerInfo->peer_choking=1;
                peerInfo->peer_interested=0;

                peerInfo->unchoking_time=0;

                // inizializzo i vettori dei rate
                peerInfo-> uploadRate.push_back(0);
                peerInfo-> downloadRate.push_back(0);
                peerInfo-> canceledRate.push_back(0);

                connection_made(*remotePeer);

                othersPeer.insert(pair<int, RemotePeer*>(*remotePeer, peerInfo));

                // inizializzo il timeout
                peerInfo->timeout= NULL; // vedi handleBitTorrentMessage()
            }

            Handshake *hmsg= new Handshake();
            hmsg->setPeerID(peerID);
            hmsg->setIKnowYou(false);
            hmsg->setBitfieldArraySize(myFile->getNumPieces());
            for (int k=0; k<myFile->getNumPieces(); k++) {
                hmsg->setBitfield(k, bitfield[k]);
            }
            multicastMessage(hmsg, "Handshake", pSet);
            delete hmsg; // cancello perche' ne viene inviata una copia
        }

    }
    delete pSet;



}

int Peer::getRemainingPieces(){

    int numRemainingPieces=0;
    cModule *sysM= simulation.getSystemModule();
    long numPieces= sysM->par("numPieces").longValue();
    for (int k=0; k<numPieces; k++) {
        if(!(myFile->havePiece(k)))
            numRemainingPieces++;
    }

    return numRemainingPieces;


}

// se leaveMsg==NULL allora e' stato richiamato dal distruttore
void Peer::handleLeaveSwarm(LeaveSwarm *leaveMsg) {

    if (leaveMsg!=NULL) { // se richiamato dal distruttore, potrebbe essere active==false
        assert(active==true);
    }

    /*
    EV << "Peer " << peerID << endl;
    EV << "Blocchi inviati: " << blocksSent << endl;
    EV << "Numero di Cancel che hanno effettivamente rimosso una richiesta: " << effectiveCancels << endl;
    EV << "Numero di blocchi duplicati: " << duplicatedBlocks << endl;
    */

    // decido in che modo rientrare
    JoinSwarm *join= new JoinSwarm();
    const char* strJoin;
    join->setEmpty(1==bernoulli(par("joinEmpty").doubleValue()));
    if (!join->getEmpty()) {
        join->setAgainRandom(1==bernoulli(par("joinAgainRandom").doubleValue()));

        if (join->getAgainRandom()) {
            strJoin= "JoinSwarm-riprendi";
        } else {
            strJoin= "JoinSwarm-random";
        }
    } else {
        join->setAgainRandom(false); // in realta' non e' significativo in questo caso
        strJoin= "JoinSwarm-vuoto";
    }
    join->setName(strJoin);

    if(active)
        tracker->trackerRequest(peerID,0,"stopped",0); // il numero di pezzi rimanenti non ha significato
    // esco dallo swarm
    active= false;

    if (leaveMsg!=NULL){
        if(myFile->haveTorrent()){ // esce un seeder

            emit(newSeederSignal, BTStatistics::delSeeder(peerID));

        }

        else{

            emit(newDownloaderSignal, BTStatistics::delDownloader(peerID));

        }
    }
    getDisplayString().setTagArg("i", 1, "red");

    while (!incoming.empty()) {
        delete incoming.back();
        incoming.pop_back();
    }
    while (!outgoing.empty()) {
        delete outgoing.back();
        outgoing.pop_back();
    }

    cancelAndDelete(outgoingMsg);
    outgoingMsg= NULL;

    cancelAndDelete(incomingMsg);
    incomingMsg= NULL;

    cancelAndDelete(startKeepAlive);
    startKeepAlive= NULL;

    cancelAndDelete(trackerRequest);
    trackerRequest=NULL;


    if (leaveMsg==NULL) {
        delete myFile;
        myFile= NULL;
    } else if (join->getEmpty() || !join->getAgainRandom()) {
        // inizio da zero o inizializzo a random
        // in entrambi i casi rimuovo tutto
        delete myFile;
        myFile= NULL;
    } // else riprendo lo stesso torrent

    // schedulo quando entrero'
    simtime_t delta= par("joinDelay");
    if (leaveMsg!=NULL) {
        scheduleAt(simTime()+delta, join);
    } else {
        delete join;
    }

    connections.clear();

    // cancello othersPeer
    for (map<int, RemotePeer *>::iterator it = othersPeer.begin(); it != othersPeer.end();){

        RemotePeer * rPeer= it->second;

        cancelAndDelete(rPeer->timeout);
        rPeer->timeout= NULL;

        delete rPeer;
        othersPeer.erase(it++); // post-incremento it per non invalidarlo
    }

    // ripulisco altre cose

    while (!piecesQueue.empty()) {
        delete piecesQueue.back();
        piecesQueue.pop_back();
    }

    requestedBlocks.clear();

}

void Peer::handleTrackerRequest(TrackerRegularRequest * msg){


    int numWanted = par("maxPeerSet").longValue()-(othersPeer.size());

    set<int> * pSet = tracker->trackerRequest(peerID, getRemainingPieces(), "started", numWanted);

    if((numWanted!=0) && (pSet!=NULL) && (pSet->size()!=0) ){ // forse il tracker mi ha restituito nuovi peer a cui fare l'handshake


        for (set<int>::iterator it= pSet->begin(); it != pSet->end();) {
                    if(othersPeer.find(*it)!=othersPeer.end()){ //conosco gia' il peer che mi ha restituito il tracker
                        pSet->erase(it++); // il post-incremento fa si che non venga invalidato l'iteratore
                    }
                    else ++it;
        }


        EV << "Peer:" << peerID << ": invio l'handshake ai seguenti peer: \n";

        for (set<int>::iterator it= pSet->begin(); it != pSet->end(); ++it) {
                EV << *it << ", ";
        }
        EV << endl;

        // popolo la mappa othersPeer
        for (set<int>::iterator remotePeer= pSet->begin(); remotePeer != pSet->end(); ++remotePeer) {
            //aggiorno la mia mappa locale con il bitfield e l'ID del peer

            RemotePeer * peerInfo = new RemotePeer();

            //inizializzo i campi associati al peer remoto

            peerInfo->btFile= new BitTorrentFile(myFile->getNumPieces()); // inizializzato di default come vuoto

           /*
            * le connessioni con un client iniziano come choked e not interested.
            * Un blocco e' scaricato dal client quando il client e' interessato ad un peer e quel peer non sta soffocando il client
            * Un blocco e' inviato da un client quando il client non sta soffocando il peer e quel peer e' interessato al client.
            * E' importante per il client tenere i suoi peer informati riguardo il suo interesse o meno verso di loro.
            * Questa informazione dovrebbe essere aggiornata anche dai peer che hanno soffocato il client che la invia in modo che essi sono a conoscenza se quel client
            * iniziera' il download quando viene unchoked (e viceversa).
            */

            peerInfo->am_choking=1;
            peerInfo->am_interested=0;
            peerInfo->pending_unchoking=0;
            peerInfo->peer_choking=1;
            peerInfo->peer_interested=0;

            peerInfo->unchoking_time=0;

            // inizializzo i vettori dei rate
            peerInfo-> uploadRate.push_back(0);
            peerInfo-> downloadRate.push_back(0);
            peerInfo-> canceledRate.push_back(0);

            connection_made(*remotePeer);

            othersPeer.insert(pair<int, RemotePeer*>(*remotePeer, peerInfo));

            // inizializzo il timeout
            peerInfo->timeout= NULL; // vedi handleBitTorrentMessage()
        }

        cModule *sysM= simulation.getSystemModule();
        long numPieces= sysM->par("numPieces").longValue();
        bool bitfield[numPieces];

        Handshake *hmsg= new Handshake();
        hmsg->setPeerID(peerID);
        hmsg->setIKnowYou(false);
        hmsg->setBitfieldArraySize(myFile->getNumPieces());
        for (int k=0; k<myFile->getNumPieces(); k++) {
            hmsg->setBitfield(k, bitfield[k]);
        }
        multicastMessage(hmsg, "Handshake", pSet);

        delete hmsg; // cancello perche' ne viene inviata una copia

    }

    trackerRequest = new TrackerRegularRequest("TrackerRegularRequest");
	
	int numRet= 0;
	
	if(pSet!=NULL){
		
		numRet=pSet->size();
	
	}
	
    if((unsigned)(numRet + othersPeer.size()) < par("minPeerSet").longValue()){ // mi sono stati restituiti pochi peer
        scheduleAt(simTime()+simTime().parse("600s"), trackerRequest);
    }
    else{
        scheduleAt(simTime()+simTime().parse("1800s"), trackerRequest);

    }

    delete pSet;


}

void Peer::handleStartKeepAlive(StartKeepAlive * msg){

    for (map<int, RemotePeer *>::iterator i = othersPeer.begin(); i != othersPeer.end(); ++i){

        forwardMessage(new KeepAlive(),(i->first),"KeepAlive");

    }


    // riavvio il timer
    startKeepAlive= new StartKeepAlive("StartKeepAlive");
    scheduleAt(simTime() + SimTime::parse("60s") , startKeepAlive);


}

void Peer::handleUpdateRate(UpdateRate *updateRateMsg) {

    // aggiorno i rate di download, di upload e di cancel dei peer

    if(areAllSeeder()){ // tutti i peer che conosco sono seeder (io compreso)

        EV << "Peer "<< peerID << ": Siamo tutti seeder (non riavvio timer per l'update rate)\n";

#ifndef NO_COUT
        cout << "> Peer " << peerID << ": Siamo tutti seeder (non riavvio timer per l'update rate)\n";
        cout.flush();
#endif

        // pulisco la lista download, upload e cancel associata ai peer attivi (quindi agli altri seeder)
        for (map<int, RemotePeer *>::iterator i = othersPeer.begin(); i != othersPeer.end(); ++i){

                list<int> * lstUp=&(i->second->uploadRate);
                list<int> * lstDown=&(i->second->downloadRate);
                list<int> * lstCancel=&(i->second->canceledRate);

                lstUp->clear();
                lstDown->clear();
                lstCancel->clear();


                // aggiungo il primo elemento alle liste associate ai peer remoti e lo inizializzo a zero
                // per poter operare correttamente nei calcoli sui rate futuri

                lstUp->push_back(0);
                lstDown->push_back(0);
                lstCancel->push_back(0);

        }

        updateRate=NULL;

        return;  // non devo riavviare il timer

    }


    EV << "Peer " << peerID << ": aggiorno i rate \n";


    for (map<int, RemotePeer *>::iterator i = othersPeer.begin(); i != othersPeer.end(); ++i){

        list<int> * lstUp=&(i->second->uploadRate);

        list<int> * lstDown=&(i->second->downloadRate);

        list<int> * lstCancel=&(i->second->canceledRate);

        int size = lstUp->size();  // anche la lista di download rate e di cancel rate hanno la stessa dimensione (aggiornate insieme)


        if(size == 3){
            //rimuovo il primo elemento (i 10 secondi non piu' necessari nel calcolo dell'algoritmo di choking)
            lstUp->pop_front();
            lstDown->pop_front();
            lstCancel->pop_front();
		}
		
        //aggiungo il nuovo elemento per calcolare il rate di upload dei prosimi 10 secondi
        lstUp->push_back(0);
        //aggiungo il nuovo elemento per calcolare il rate di download dei prosimi 10 secondi
        lstDown->push_back(0);
        //aggiungo il nuovo elemento per calcolare il rate di cancel dei prosimi 10 secondi
        lstCancel->push_back(0);


        EV << "Peer " << peerID << ": il peer " << (i->first) << " ha gli upload rate seguenti:\n";

        for(list<int>::iterator it=lstUp->begin(); it!=lstUp->end(); ++it){

            EV << "- " << (*it) << "\n";

        }

        EV << "\n";

        EV << "Peer "<< peerID << ": il peer " << (i->first) << " ha i download rate seguenti:\n";

        for(list<int>::iterator it=lstDown->begin(); it!=lstDown->end(); ++it){

            EV << "- " << (*it) << "\n";

        }

        EV << "\n";
        
        if(maliciousEnabled){

        	EV << "Peer "<< peerID << ": il peer " << (i->first) << " ha i cancel rate seguenti:\n";

        	for(list<int>::iterator it=lstCancel->begin(); it!=lstCancel->end(); ++it){

           	 	EV << "- " << (*it) << "\n";

        	}

        	EV << "\n";
       	}


    }


    if(updateRate!=NULL && updateRate->isScheduled())
            cancelAndDelete(updateRate); //  se era nella coda degli auto-messaggi verra' rimosso


    updateRate=new UpdateRate("UpdateRate");

    // riavvio il timer
    scheduleAt(simTime()+simTime().parse("10s"), updateRate);

}

void Peer::handleChokingAlgorithm(ChokingAlgorithm * chokingAlgMsg) {
	
	//eseguo l'algoritmo di choking

    EV << "Peer " << peerID << ": eseguo l'algoritmo di choking\n\n";


    round_robin();

    //chokingAlgorithm(chokingAlgMsg->getSec());


}

void Peer::handlePeerTimeout(PeerTimeout *timeoutMsg) {

    int peer=timeoutMsg->getRemotePeerID();

    EV << "Peer " << peerID << ": ho ricevuto il timeout per il peer " << peer <<"\n";

    map<int, RemotePeer* >::iterator iter= othersPeer.find(peer);

    if (iter != othersPeer.end()) {
        // trovato


        if(othersPeer[peer]->myRequestedBlocks.size()!=0){ //avevo richiesto dei blocchi al peer remoto

                EV << "Peer " << peerID << ": ho ancora delle richieste a cui il peer " << peer <<" non a mai risposto:\n";


                for (map<int, set<int> >::iterator i = othersPeer[peer]->myRequestedBlocks.begin(); i != othersPeer[peer]->myRequestedBlocks.end(); ++i){

                    // itero sui pezzi del peer che non e' piu' presente

                    EV << "+ (" << (i->first) << " , ";

                    for (set<int>::iterator j = i->second.begin();j!=i->second.end();++j){

                        bool found = false;

                        // itero sui blocchi del pezzo i

                        EV << (*j) << " - ";


                        for(map<int, RemotePeer * >::iterator k = othersPeer.begin(); k != othersPeer.end(); ++k){

                            // itero sugli altri peer , il peer k ha il pezzo i e il blocco j ?

                            if(!(k->second->peer_choking)){

                                if(!((k->second->myRequestedBlocks.find(i->first) != k->second->myRequestedBlocks.end())
                                        && (k->second->myRequestedBlocks[i->first].find(*j) != k->second->myRequestedBlocks[i->first].end()))){
                                    found=true;
                                    break;
                                }


                            }


                        }


                        if(found==false){
                            /* rimuovo solo il blocco, lascio il pezzo nella mappa in modo che grazie a selectPiece() venga richiesto ancora */
                            requestedBlocks[i->first].erase(*j);
                        }

                    }



                    EV << ")\n";

                }

                othersPeer[peer]->myRequestedBlocks.clear();


                EV << "Peer " << peerID << ": Potrei aver rimosso delle richieste da requestedBlocks, che adesso contiene:\n";

                for (map<int, set<int> >::iterator k =requestedBlocks.begin();k!=requestedBlocks.end();++k){

                    EV << "+ (" << (k->first) << " , ";

                                for (set<int>::iterator it = k->second.begin();it!=k->second.end();++it){

                                    EV << (*it) << " - ";

                                }

                    EV << ")\n";

                }

        }


        // rimuovo connessione
        connection_lost(peer);  //la rimozione del peer da othersPeer viene gia' fatta in connection_lost


        EV << "Peer " << peerID << ": e' scaduto il timer relativo alla connessione con il peer " << peer <<" (lo rimuovo dai peer conosciuti)\n";

#ifndef NO_COUT
        cout << "> Peer " << peerID << ": e' scaduto il timer relativo alla connessione con il peer " << peer <<" (lo rimuovo dai peer conosciuti) ***\n";
        cout.flush();
#endif

        // NB: rPeer->timeout==timeoutMsg => delete del timeout in handleInternalMessage()

    }

    if((unsigned)othersPeer.size()<par("minPeerSet").longValue() && (!(myFile->haveTorrent()))){

        set<int> * pSet= tracker->trackerRequest(peerID,getRemainingPieces(),"regular",par("maxPeerSet").longValue()-othersPeer.size());

        if((pSet!=NULL) && (pSet->size()!=0)){
            for(set<int>::iterator it = pSet->begin();it!=pSet->end();){

                if(othersPeer.find(*it)!= othersPeer.end()){
                    // il peer e' gia' presente in othersPeer
                    pSet->erase(it++);
                }
                else ++it;
            }

            EV << "Invio l'handshake ai seguenti peer: \n";
            for (set<int>::iterator it= pSet->begin(); it != pSet->end(); ++it) {
                if ((*it)!=peerID){
                     EV << *it << ", ";
                }
            }
            EV << endl;
           int np= myFile->getNumPieces();
           bool bitfield[np];
           for (int k=0; k<np; k++) {
                bitfield[k]= myFile->havePiece(k);
            }

             Handshake *hmsg= new Handshake();
             hmsg->setPeerID(peerID);
             hmsg->setIKnowYou(false);
             hmsg->setBitfieldArraySize(myFile->getNumPieces());
             for (int k=0; k<myFile->getNumPieces(); k++) {
              hmsg->setBitfield(k, bitfield[k]);
            }
            multicastMessage(hmsg, "Handshake", pSet);
            delete hmsg; // cancello perche' ne viene inviata una copia
        }


        cancelAndDelete(trackerRequest);
        trackerRequest = new TrackerRegularRequest("TrackerRegularRequest");
        
        int numRet= 0;
	
		if(pSet!=NULL){
		
			numRet=pSet->size();
	
		}

        if((unsigned)(numRet + othersPeer.size()) < par("minPeerSet").longValue()){ // mi sono stati restituiti pochi peer
            scheduleAt(simTime()+simTime().parse("600s"), trackerRequest);
        }
        else{
            scheduleAt(simTime()+simTime().parse("1800s"), trackerRequest);

        }

        delete pSet;


    }

}


void Peer::handleExtractIncomingMsg(ExtractIncomingMsg * msg) {
    // estraggo un messaggio dalla coda e consegno al "processo locale"

    /* siccome possono essere stati rimosse delle richieste prima
     * che il timer ExtractIncomingMessage arrivasse la coda incoming potrebbe essere vuota
     */
    if(incoming.size()==0){
        incomingMsg=NULL;
        return;
    }

    BitTorrentMsg *btMsg= popIncomingMsg();
    if (active) {
        // gestisco i messaggi solo se sono attivo
        handleBitTorrentMessage(btMsg);
    } else {
        delete btMsg;
    }
}

void Peer::handleExtractOutgoingMsg(ExtractOutgoingMsg * msg) {
    // estraggo un messaggio dalla coda e lo invio tramite send()

    if(outgoing.size()==0){
        outgoingMsg=NULL;
        return;
     }

    popOutgoingMsgAndSend();
}

// // //
// GESTIONE DELL'INVIO DEI MESSAGGI A PEER REMOTI (ES. MULTICAST)

inline cChannel *Peer::getTransmissionChannel(int dest) {
    return gate("io$o")->getTransmissionChannel();
}

inline void Peer::mySend(BitTorrentMsg *msg) {
    send(msg, "io$o");
}

void Peer::forwardMessage(BitTorrentMsg *msg, int dest, string msgName, int varLen){

    stringstream strStream;

    strStream << msgName << " - da " << peerID << " a " << dest;

    msg->setName(strStream.str().c_str());

    //EV << "Peer "<< peerID << ": invio messaggio " << (msg->getName()) << " al peer " << dest << "\n";

    setSrcDst(dest, msg);

    // metto a posto la dimensione del pacchetto
    int len= msg->getBtPayloadLength();
    if (varLen>0) {
        len= len+varLen;
        msg->setBtPayloadLength(len);
    }
    msg->setByteLength(len);

    // invio del pacchetto
    pushOutgoingMsg(msg);
}


void Peer::setSrcDst(int dest,BitTorrentMsg *msg) {

    msg->setSource(peerID);
    msg->setDestination(dest);
}

void Peer::multicastMessage(BitTorrentMsg *msg, string msgName, int varLen){

    int dest;
    for (map<int,RemotePeer * >::iterator it= othersPeer.begin(); it != othersPeer.end(); ++it) {
        dest= it->first;
        if(dest!=peerID){ //non invio il messaggio a me stesso
            // Duplicate message and send the copy.
            BitTorrentMsg *copy = (BitTorrentMsg *) msg->dup();
            forwardMessage(copy,dest, msgName, varLen);
        }
    }

}


void Peer::multicastMessage(BitTorrentMsg *msg, vector<int> excludedDests, string msgName, int varLen){

    int dest;
    for (map<int,RemotePeer * >::iterator it= othersPeer.begin(); it != othersPeer.end(); ++it) {
        dest= it->first;
        if(dest!=peerID && //non invio il messaggio a me stesso...
                find(excludedDests.begin(), excludedDests.end(), dest)==excludedDests.end()){ //...ed alle destinazioni escluse
            // Duplicate message and send the copy.
            BitTorrentMsg *copy = (BitTorrentMsg *) msg->dup();
            forwardMessage(copy,dest, msgName, varLen);
        }
    }

}

void Peer::multicastMessage(BitTorrentMsg *msg, string msgName, set<int> * dests, int varLen){


    int dest;
    for (set<int>::iterator it= dests->begin(); it != dests->end(); ++it) {
        dest= *it;
        if(dest!=peerID){ //non invio il messaggio a me stesso
            // Duplicate message and send the copy.
            BitTorrentMsg *copy = (BitTorrentMsg *) msg->dup();
            forwardMessage(copy,dest, msgName, varLen);
        }
    }



}


// // //
// POLITICHE DI SELEZIONE DEL PEZZO DA SCARICARE

/*
 * Strict Priority:
 *
 * completare l'assemblaggio di un pezzo prima di chiederne un altro
 *
 * - solo i pezzi completi possono essere scambiati con gli altri peer
 *
 * Questa politica e' seguita sia durante Random First Piece che durante Rarest First
 *
 * -----------------------
 *
 * Random First Piece:
 *
 * scelta di un pezzo random in fase di bootstrap
 *
 * - inizialmente un leecher non possiede alcun pezzo e quindi non puo' offrire niente agli altri peer dello swarm
 *   e' importante che acquisica un pezzo prima possibile per poter iniziare a negoziare pezzi con altri peer (algoritmo di unchoking)
 *
 * - per scaricare i primi 4 pezzi, si effettua una scelta casuale del pezzo da scaricare
 *
 * dopo si passa al Rarest First
 *
 * -----------------------
 *
 * Rarest First:
 *
 * scaricare i pezzi piu' rari per primi
 *
 * - ogni peer puo' calcolare la disponibilita' di ogni pezzo (quanti peer nel Peer Set possiedono quel pezzo)
 *
 * -Rarest Piece Set: l'insieme di pezzi con disponibilita' minima rispetto alla conoscenza locale del peer
 * (aggiornato ogni volta che il peer riceve un messaggio di Have oppure un Bitfield)
 *
 *  - seleziona in modo casuale un peer tra quelli del Rarest Piece Set.
 *
 * -----------------------
 *
 * End Game:
 *
 * broadcast per i blocchi rimanenti quando il download del file sta per terminare
 *
 * - gli ultimi pezzi del file da scaricare vengono richiesti a tutti i peer che lo possiedono
 *
 * - per evitare di sprecare banda, quando il pezzo richiesto viene ricevuto da un peer,
 *   i download avviati in parallelo vengono annullati
 *   
 * - Un client entra nella modalita' end game quando tutti i blocchi sono stati richiesti.
 *
 */

/**
 * Controlla le parti mancanti del file e richiede il prossimo pezzo in base alla politica di selezione
 * 
 * Restituisce -1 nel caso della politica end game 
 */
int Peer::selectPiece(){

	// Strict Priority
    /*
     * Verifico nelle richieste pendenti se ci sono pezzi di cui non sono stati
     * richiesti tutti i blocchi.
     */

    if(myFile->haveTorrent()){ //sono un seeder non mi serve alcun pezzo
        return -1;
    }

    if(policy==END_GAME && !maliciousEnabled){ //se sono gia' entrato nella politica END_GAME devo continuare ad usare questa
            return -1;
    }

    // potrebbe essere accaduto che sia stato richiesto e quindi aggiunto alla mappa requestedBlocks un pezzo non ritornato
    // dalla politica, per cui se e' presente un pezzo precedente ritornato dalla politica cerco di favorire quest'ultimo.
/*// TODO ripristinare beta
    if(lastPieceSel!=-1 && requestedBlocks.find(lastPieceSel)!=requestedBlocks.end()){
        //l'ultimo pezzo che e' stato restituito dalla politica non e' ancora stato ottenuto
        // (e' ancora presente in requestedBlocks)

        for (int k=0; k<myFile->getNumBlocksPerPiece(); k++){

            if(!myFile->haveBlock(lastPieceSel,k) &&
                    requestedBlocks[lastPieceSel].find(k)==requestedBlocks[lastPieceSel].end()){
                    //non ho questo blocco e non e' stato richiesto a nessun peer
                    return lastPieceSel;
            }

        }

    } else if (lastPieceSel!=-1) {
        // non ho ancora finito di richiedere l'ultimo pezzo
        return lastPieceSel;
    }*/

    for (map<int, set<int> >::iterator i = requestedBlocks.begin(); i != requestedBlocks.end(); ++i) {
        bool tryNextPiece=false;
        set<int> blocks= i->second;

        if(myFile->havePiece(i->first)){

            // ho gia' ottenuto questo pezzo
            // non dovrebbe stare tra i blocchi richiesti
            // lo rimuovo
            requestedBlocks.erase(i++);
            continue;

        }

        for (int k=0; k<myFile->getNumBlocksPerPiece(); k++){
            if (!myFile->haveBlock(i->first,k)) {
                // non ho questo blocco
                // l'ho richiesto a qualcuno?

                if (blocks.find(k) == blocks.end()) {
                    // questo blocco non e' stato richiesto ad alcun peer
                    // seleziono questo pezzo se qualcuno lo possiede
                    for(map<int, RemotePeer *>::iterator it = othersPeer.begin();it!= othersPeer.end();++it)  {

                        if(it->second->btFile->havePiece(i->first)){
                            lastPieceSel=i->first;
                            return i->first;
                        }


                    }

                    tryNextPiece= true;

                } // else questo blocco e' gia' stato richiesto
            } // else ho gia' questo blocco, non devo richiederlo

            if(tryNextPiece) break;
        }
    }
    // se arrivo qui significa che non ci sono pezzi di cui non sono stati richiesti tutti i blocchi.
    // oppure tutti i blocchi dei pezzi in requestedBlocks sono gia' stati richiesti
    if (maliciousEnabled && piecesQueue.empty()) {
        // restituisco il valore speciale -5 (ma va!)
        return -5;
    }


    // Eventualmente cambio politica
    switch (policy) {
    case RAREST_FIRST:
		if (piecesQueue.empty()) {
            /*
             * Se arrivo qui:
             * - non ci sono pezzi di cui non sono stati richiesti tutti blocchi
             *   (sono stati richiesti tutti i blocchi dei pezzi richiesti)
             * - non ci sono nuovi pezzi da richiedere
             *
             * Quindi significa che sono stati richiesti tutti i blocchi => entro nella modalita' end game
             */
            if(!maliciousEnabled){ //con i peer maliziosi end_game e' gia' usato
                policy= END_GAME;
                EV << "Peer " << peerID << ": uso la politica END_GAME\n";

            }
        }
        break;

    case RANDOM_FIRST_PIECE:

        if ( (unsigned)(myFile->getNumPieces() - piecesQueue.size()) > par("randomPhaseLength").longValue() ) {
            // sono gia' stati selezionati "randomPhaseLength" pezzi => passo alla politica rarest first
            policy= RAREST_FIRST;
            EV << "Peer " << peerID << ": uso la politica RAREST_FIRST\n";
            updateQueue();
        }

        break;

    case END_GAME:
    	// una volta arrivato qui, mantengo questa politica
        break;
    }
    
	if ( (policy!=END_GAME) && piecesQueue.empty() && !maliciousEnabled) {
        // forzo il passaggio in end game
        policy= END_GAME;
        EV << "Peer " << peerID << ": uso la politica END_GAME\n";
    }
    
    // Selezione del pezzo
 	int sel;
    PieceScore *selPiece;
    bool found = false;
    int count = 0;

    switch (policy) {

    case RAREST_FIRST:
    case RANDOM_FIRST_PIECE:

        // seleziono il prossimo pezzo da richiedere
        int queueIndex;
        int selRange;
        selRange= par("randomPieceSelectionSize").longValue();

        while(!found && ((unsigned)count<piecesQueue.size())){

            sel= -1;
            if (piecesQueue.size() < (unsigned)selRange) {
                selRange= piecesQueue.size();
            }
            queueIndex= intuniform(0, selRange-1);
            // scorro fino alla posizione queueIndex
            int k= 0;
            for(vector<PieceScore *>::reverse_iterator it = piecesQueue.rbegin();it!= piecesQueue.rend();++it)  {
                if (k!=queueIndex) {
                    k++;
                    continue;
                } else {
                    // trovato
                    selPiece= *it;
                    sel= selPiece->index;
                    // ma non lo rimuovo dalla coda

                    // controllo se qualcuno possiede questo pezzo
                     for(map<int, RemotePeer *>::iterator it2 = othersPeer.begin();it2!= othersPeer.end();++it2)  {

                         if(it2->second->btFile->havePiece(sel)){
                             found=true;
                             /* metto il pezzo selezionato in cima alla coda
                             * perche' altrimenti non viene gestita correttamente la rimozione
                             * in requestPiece()
                             */
                             /*it++;
                             piecesQueue.erase(it.base()); // rimuovo
                             piecesQueue.insert(piecesQueue.end(), selPiece); // inserisco in coda*/
                             break;
                         }
                     }

                     if(!found){ // questo pezzo non lo possiede nessuno, inutile richiedere questo

                         EV << "Nessuno possiede " << (*it)->index << ", \tindice selezionato" << queueIndex << ", \trange attuale\t" << selRange << endl;
                         EV << "Peer che conosco: ";
                         for(map<int, RemotePeer *>::iterator it2 = othersPeer.begin();it2!= othersPeer.end();++it2)  {
                             EV << it2->first << ", ";
                         }
                         EV << endl;

                         // aumento il range su cui seleziono a random
                         selRange++;
                     }

                    break;
                }
            }

            assert(sel!=-1);

             count++;
        }
        if (!found) {
            // alla fine del for se nessuno possiede alcun pezzo, la coda e' come era all'inizio
            // e seleziono ugualmente il primo
            selPiece= piecesQueue.back();
            sel= selPiece->index;
        }
        break;

    case END_GAME:
        sel= -1;
        break;
    }

    if(sel!=-1)
        lastPieceSel=sel;

    if(maliciousEnabled)
        assert(sel!=-1);


    return sel;
}

void Peer::initQueue() {
    EV << "Peer " << peerID << ": initQueue()\n";
    // inizializza tutti con score 0

    while (!piecesQueue.empty()) { // ripulisco
        delete piecesQueue.back();
        piecesQueue.pop_back();
    }

    // coda vuota => e' la prima volta che viene richiamato
    piecesQueue.reserve(myFile->getNumPieces()+1); // riservo tutto lo spazio necessario

    // popolo la coda
    for (int k=0; k<myFile->getNumPieces(); k++) {

        if (!myFile->havePiece(k)) {
            // non ho questo pezzo => aggiungo alla coda
            // inserimento in coda: tutto con lo stesso score
            piecesQueue.push_back(new PieceScore(k, 0));
        }
    }

    // eventualmente faccio delle operazioni sulla coda
    switch (policy) {
    case RAREST_FIRST:
        // nulla di fare
        break;
    case RANDOM_FIRST_PIECE:
        // ora devo mescolare la coda
        random_shuffle(piecesQueue.begin(), piecesQueue.end(), randomPieceIndex);
        // mescolo solo la prima volta
        break;
    case END_GAME:
        // nulla di fare
        break;
    }

    // aggiorno
    updateQueue();
}

void Peer::updateQueue() {
    EV << "Peer " << peerID << ": updateQueue()\n";
    // ricalcolo la coda dei pezzi da scaricare

    switch (policy) {
    case RAREST_FIRST:

        // calcolo la popolarita' di ogni pezzo
        for (vector<PieceScore*>::reverse_iterator p = piecesQueue.rbegin(); p != piecesQueue.rend(); ++p){
            // scorro gli elementi della coda
            // azzero lo score e poi lo ricalcolo

            (*p)->score=0;

            for (map<int, RemotePeer *>::iterator i = othersPeer.begin(); i != othersPeer.end(); ++i) {
                if (i->second->btFile->havePiece((*p)->index)) {
                    // questo peer ha il pezzo "p"
                    (*p)->score++;
                }
            }
        }

        // prima metto in disordine in pezzi. Questo serve a mescolare i pezzi con lo stesso score
        random_shuffle(piecesQueue.begin(), piecesQueue.end(), randomPieceIndex);
        // poi gli ordino in base allo score
        stable_sort(piecesQueue.begin(), piecesQueue.end(), PieceScore::comparator);

        break;

    case RANDOM_FIRST_PIECE:
        // nulla da fare
        break;


    case END_GAME:
		// nulla da fare
        break;
    }


    EV << "Peer " << peerID << ", updateQueue(): piecesQueue -> [ ";
    for (vector<PieceScore*>::reverse_iterator p = piecesQueue.rbegin(); p != piecesQueue.rend(); ++p){
        EV << "(i " << ((*p)->index) << ", s " << ((*p)->score) << "), ";
    }
    EV << " ]\n";
}



int randomPieceIndex(int n) {
   	//return intuniform(0,n-1);
   	//return std::rand()%i;
    return uniform(0,1)*n;
}




// // //
// ALGORITMO DI CHOCKING/UNCHOCKING

// esegue unchoke ogni 10s e optimistic unchoke ogni 30s
void Peer::round_robin(){

    if(areAllSeeder()){ // tutti i peer attivi sono seeder (io compreso)

                chokingMsg=NULL;

                EV << "Peer "<< peerID << ": Siamo tutti seeder (non riavvio timer per il choking)\n";

    #ifndef NO_COUT
                cout << "> Peer " << peerID << ": Siamo tutti seeder (non riavvio timer per il choking)\n";
                cout.flush();
    #endif

                return;  // non devo riavviare il timer

            }

    if(!someoneIsInterested()){ // nessuno e' interessato a me (possiedono gia' i pezzi di file che io ho)

                chokingMsg=NULL;

                EV << "Peer "<< peerID << ": Nessuno e' interessato a me (non riavvio timer per il choking)\n";

    #ifndef NO_COUT
                cout << "> Peer " << peerID << ": Nessuno e' interessato a me (non riavvio timer per il choking)\n";
                cout.flush();
    #endif

                return;  // non devo riavviare il timer
    }

    chokingMsg=new ChokingAlgorithm("ChokingAlgorithm");
    scheduleAt(simTime()+simTime().parse("10s"), chokingMsg); // lo schedulo per eseguirlo fra altri 10 secondi
    chokingCount += 1; // incrementato ogni volta che viene eseguito l'algoritmo

    if(myFile->haveTorrent()){ // sono seeder
        EV << "Peer " << peerID << ": sono seeder, eseguo l'algoritmo rechoke_seed()\n";
        rechoke_seed(true); // eseguo l'algoritmo di choking per il seeder forzando nuovi unchokes()
        return;
    }
    if (chokingCount % 3 == 0) { // 1 volta ogni 30 secondi eseguo l'optimistic unchoking (sono un leecher)
        for (list<int>::iterator i = connections.begin(); i!=connections.end();++i){
            if(othersPeer[*i]->am_choking && othersPeer[*i]->peer_interested){ // se il peer e' soffocato ed interessato
                /* lo metto ad inizio lista connections
                * e quelli che erano prima di lui vanno in fondo.
                * Non c'e' un flag optimistic unchoke, il peer optimistic unchoked e' implicitamente il primo della
                * lista connections.
                * Diversi optimistic unchoke possono essere eseguiti dall'algoritmo in base al numero minimo di upload
                * e all'anti snubbing, eseguiti all'interno del metodo rechoke().
               */
                EV << "Peer " << peerID << ": il peer " << (*i) << " e' stato selezionato come optimistic unchoked peer\n";
                list<int> before;
                before.splice(before.begin(),connections,i,connections.end());
                connections.splice(connections.begin(),before);
                EV << "Peer " << peerID << ": la lista connections iniziale e' cosi' composta:\n";
                for (list<int>::iterator i = connections.begin(); i!=connections.end();++i){

                    EV << *i <<", ";

                }
                EV << "\n";


                break;
            }
        }
    }
    EV << "Peer " << peerID << ": sono leecher, eseguo l'algoritmo rechoke()\n";
    rechoke(); // se non sono seeder eseguo l'algoritmo di choking per il leecher
}

//unchoke ed optimistic unchoke quando il peer locale e' un leecher
void Peer::rechoke(){ // algoritmo di choking per il leecher
    if(myFile->haveTorrent()){// controllo se sono un seeder perche'
        rechoke_seed(false); // rechoke() viene chiamato quando un peer a cui e' stato fatto unchoking diventa not interested
        return;              // oppure quando diventa Interested un peer che era pending_unchoking
                            // oppure perche' il metodo rechoke() e' chiamato da connection_lost()
    }


    EV << "Peer " << peerID << ": i peer che sono interessati a me sono:\n";
    for (map<int, RemotePeer *>::iterator i = othersPeer.begin(); i != othersPeer.end(); ++i){

        if(i->second->peer_interested){

            EV << i->first << ", ";
        }

    }

    EV << "\n";

    /* ordino i peer interested e not snubbed in base all'upload rate per selezionare i 4 migliori upload su cui effettuare l'unchoke
     * il risultato del sorting viene messo nel vettore preferred, il primo della lista e' il peer con l'upload rate piu' alto
     */
    vector<RateStruct *> preferred;  //lista dei peer preferiti,

    int pos1=0;
    for (list<int>::iterator i = connections.begin(); i!=connections.end();++i,pos1++){

            RateStruct * ordUp = new RateStruct();

            ordUp->peer=(*i);
            ordUp->pos=pos1;

            if(othersPeer[*i]->uploadRate.size()<2){ //sono passati solo 10 secondi, considero quelli

                ordUp->rateLastTwenty=*(othersPeer[*i]->uploadRate.rbegin());
                ordUp->rateLastThirty=*(othersPeer[*i]->uploadRate.rbegin());
                ordUp->rateCancelTwenty=*(othersPeer[*i]->canceledRate.rbegin());

            }
            else if(othersPeer[*i]->uploadRate.size()==2){ // sono passati 20 secondi
                ordUp->rateLastTwenty=*(othersPeer[*i]->uploadRate.rbegin())+*(++(othersPeer[*i]->uploadRate.rbegin()));
                ordUp->rateCancelTwenty=*(othersPeer[*i]->canceledRate.rbegin())+*(++(othersPeer[*i]->canceledRate.rbegin()));
                ordUp->rateLastThirty=*(othersPeer[*i]->uploadRate.rbegin())+*(++(othersPeer[*i]->uploadRate.rbegin()));


            }else { // sono gia' passati 30 secondi

                ordUp->rateLastTwenty=*(othersPeer[*i]->uploadRate.rbegin())+*(++(othersPeer[*i]->uploadRate.rbegin()));
                ordUp->rateCancelTwenty=*(othersPeer[*i]->canceledRate.rbegin())+*(++(othersPeer[*i]->canceledRate.rbegin()));
                ordUp->rateLastThirty=*(othersPeer[*i]->uploadRate.rbegin())+*(++(othersPeer[*i]->uploadRate.rbegin())) + *(++(++(othersPeer[*i]->uploadRate.rbegin())));
            }


        int rateLastThirty= (ordUp->rateLastThirty)>1; // mi deve avere inviato almeno un blocco negli ultimi 3 round (30 secondi)

        if(othersPeer[*i]->peer_interested && rateLastThirty){ //se e' interessato a me e mi ha inviato qualcosa lo aggiungo ai preferiti
            preferred.push_back(ordUp);
        }
        else delete ordUp;
    }

    // ordino la lista preferred secondo i peer che hanno un upload rate piu' alto
    if(!maliciousEnabled)
        stable_sort(preferred.begin(), preferred.end(), RateStruct::comparator);
    else {  // maliciousEnabled
        if(par("unchokeWithCancelNum").boolValue()){
        // il vettore viene ordinato secondo i peer che hanno un upload rate piu' alto ma che hanno ricevuto meno Cancel
            stable_sort(preferred.begin(), preferred.end(),RateStruct::endGameComparator);
        }
        else
            stable_sort(preferred.begin(), preferred.end(), RateStruct::comparator);
    }
    /* prefcount e' il numero di unchoking da eseguire, escludendo gli unchoking ottimistici
     * siccome max_uploads e' per default 4 quando preferred.size()>2, prefcount=3
     */
    int prefcount = min((int)preferred.size(), (int)(par("max_uploads").longValue()-1));
    EV << "Peer " << peerID << ": prefcount = " << prefcount <<"\n";
    vector<int> mask (connections.size(),0); //inizializzo a zero l'array mask di lunghezza connections
    int n=0;
    EV << "Peer " << peerID << ": la lista preferred dei peer e' cosi' composta:\n";
    for(vector<RateStruct *>::iterator it = preferred.begin();it!=preferred.end() && n<prefcount;it++,n++){
        mask[(*it)->pos] = 1; //mask[i]=1 quando la connessione deve essere unchoked

        EV << (*it)->peer << " con upload rate= " <<  (*it)->rateLastTwenty << ", \n";

    }
    EV << "\n";

    while (!preferred.empty()) {
        delete preferred.back();
        preferred.pop_back();
    }

    /*
     * min_uploads numero di upload per ulteriori unchoking ottimistici (default a 2)
     * min_uploads probabilmente non ha senso che sia maggiore di 4
     * count e' il numero di unchoking ottimistici da eseguire. Se molte connessioni sono snubbed
     * (peer che non hanno inviato blocchi), cioe' prefcount=0 allora count e' uguale a 2 per
     * min_upload=2. count e' uguale a 1 quando prefcount>0
     */
    int count = max(1, (int)par("min_uploads").longValue() - prefcount);
    EV << "Peer " << peerID << ": count = " << count <<"\n";
    int pos=0;
    for (list<int>::iterator i = connections.begin(); i!=connections.end();++i,pos++){

        EV << "connection -> " << *i << ", pos ->" << pos << ", mask[pos] -> " << mask[pos] <<"\n";


        if(mask[pos]){ // regular unchoked peer

            if (othersPeer[(*i)]->am_choking) {

                EV << "Peer " << peerID << ": Faccio il regular unchoking del peer " << ((*i)) << "\n";

                othersPeer[(*i)]->am_choking=false;
                othersPeer[(*i)]->pending_unchoking=false;
                othersPeer[(*i)]->unchoking_time=chokingCount;

                // invio al peer il messaggio di Unchoking
                Unchoke * unchokeMsg= new Unchoke();
                forwardMessage(unchokeMsg,(*i),"Unchoke");

            }

        }else if(count > 0){

            /* in caso il peer scelto come optimistic unchoked
             * non fosse interessato, viene messo nello stato
             * pnding_unchoking ma non ne viene fatto l'unchoking
             * (non viene contato come optimistic unchoked peer --> count non viene decrementato)
             * Alla fine solo 4 peer saranno unchoked ed interested. Tutti i peer che sono stati
             * scelti come pending_unchoking quando inviano un messaggio Interested, il metodo rechoke e'
             * chiamato immediatamente, senza bisogno di attendere i 10 secondi
             *
             * Nel caso standard con un unico peer optimistic unchoked,
             * questo sara' il primo della lista connections.
             * Quindi e' sempre contato come optimistic unchoked a meno che non sia uno dei
             * 3 migliori peer che vengono scelti come regular unchoked peer. In quel caso sara' scelto come
             * regular unchoked peer e verra' scelto un altro peer (se interessato) come optimistic unchoked.
             *
             * Siccome il primo peer della lista connections non cambia finche non viene eseguito
             * un nuovo optimistic unchoking (ogni 30s) anche se rechoke() viene chiamato ogni 10s
             * il peer scelto come optimistic unchoked rimarra unchoked durante i 30s (se continua a
             * rimanere interessato)
             * In 2 casi un nuovo peer viene scelto nella lista connections entro i 10 secondi:
             * 1) uno dei 4 peer unchoked non e' piu' interessato
             * 2) l'optimistic unchoked peer diventa uno dei 3 migliori peer
             *
             */

            if(othersPeer[*i]->peer_interested){ // se e' interessato

                count -= 1; // deve stare fuori dal blocco if (othersPeer[(*i)]->am_choking)

                if (othersPeer[(*i)]->am_choking) {


                    EV << "Peer " << peerID << ": Faccio l'optimistic unchoking del peer " << ((*i)) << "\n";

                othersPeer[(*i)]->am_choking=false;
                othersPeer[*i]->pending_unchoking=false;
                othersPeer[*i]->unchoking_time=chokingCount;

                // invio al peer il messaggio di Unchoking
                Unchoke * unchokeMsg= new Unchoke();
                forwardMessage(unchokeMsg,(*i),"Unchoke");

                }
                else
                     EV << "Peer " << peerID << ": il peer " << ((*i)) << " e' gia' unchoked\n";


            }

            else{

                othersPeer[(*i)]->am_choking=true;
                othersPeer[*i]->pending_unchoking=true;
                othersPeer[*i]->unchoking_time=chokingCount;

            }

        }else{
            if(!(othersPeer[(*i)]->am_choking)){

                    EV << "Peer " << peerID << ": Faccio il choking del peer " << (*i) <<"\n";

                    othersPeer[(*i)]->am_choking=true;
                    othersPeer[*i]->pending_unchoking=false;

                    // rimuovo dalla coda in ingresso le richieste che mi ha fatto il peer
                    for (list<MsgStruct *>::iterator ms = incoming.begin(); ms!=incoming.end();){
                        if (((*ms)->sourcePeer==(*i)) && ((*ms)->isRequest)) {

                            Request * msg = dynamic_cast<Request *>((*ms)->popMsg());

                            list<Request*> * queue = &(othersPeer[*i]->requestsQueue);

                            for (list<Request*>::iterator it = (*queue).begin(); it!=(*queue).end();++it){

                                if((*it)->getBlockIndex() == msg->getBlockIndex() &&
                                        (*it)->getPieceIndex() == msg->getPieceIndex()){
                                    delete(*it);
                                    othersPeer[*i]->requestsQueue.erase(it++);
                                    break;
                                }

                            }


                            delete (*ms); // distruggo la struttura E l'eventuale messaggio contenuto

                            ms= incoming.erase(ms); // rimuovo dalla coda


                        } else {
                            ++ms; // avanzo
                        }
                    }

                    // rimuovo dalla coda in uscita i pezzi che sto mandando al peer
                    for (list<BitTorrentMsg *>::iterator ms = outgoing.begin(); ms!=outgoing.end();){
                        if (((*ms)->getDestination()==(*i)) && (dynamic_cast<Piece *>(*ms))) {

                            Piece * msg = dynamic_cast<Piece *>(*ms);

                            list<Request*> * queue = &(othersPeer[*i]->requestsQueue);

                            for (list<Request*>::iterator it = (*queue).begin(); it!=(*queue).end();++it){

                                if((*it)->getBlockIndex() == msg->getBlockIndex() &&
                                        (*it)->getPieceIndex() == msg->getPieceIndex()){
                                    delete(*it);
                                    othersPeer[*i]->requestsQueue.erase(it++);
                                    break;
                                }

                            }

                            delete (*ms); // distruggo il messaggio contenuto

                            ms = outgoing.erase(ms); // rimuovo dalla coda


                        } else {
                            ++ms; // avanzo
                        }
                    }


                    // invio al peer il messaggio di Choking
                    Choke * chokeMsg= new Choke();
                    forwardMessage(chokeMsg,(*i),"Choke");


          }
        }
    }
}


/* optimistic unchoke quando il peer e' un seeder
 * e' chiamato con force_new_unchokes=true da round_robin() ogni 10 secondi
 * e con force_new_unchokes=false da rechoke() ogni volta che un peer a cui e' stato fatto unchoking
 * diventa NotInterested oppure quando diventa Interested un peer che era pending_unchoking
 * oppure quando il metodo rechoke() e' chiamato da connection_lost()
 *
 */
void Peer::rechoke_seed(bool force_new_unchokes){ //algoritmo di choking per il seeder

    EV << "Peer " << peerID << ": i peer che sono interessati a me sono:\n";
        for (map<int, RemotePeer *>::iterator i = othersPeer.begin(); i != othersPeer.end(); ++i){

            if(i->second->peer_interested){

                EV << i->first << ", ";
            }

        }
        EV << "\n";

    int num_force_unchokes=0;

    if(force_new_unchokes){ // se devo forzare nuovi unchoke
        /* i e' il numero di unchokes da eseguire in un periodo di 30s
         * siccome rechoke_seed e' chiamato ogni 10s,
         * il numero i di unchoke e' diviso quindi sui 3 periodi da 10s
         * per max_uploads==4, i==2
        */
        int i = (par("max_uploads").longValue() + 2) / 3;

        EV << "i = " << i<< "\n";


        /*
         *  num_force_unchokes e' il numero di unchoke da eseguire su questo intervallo di 10s.
         *  Considera i in modo che 3 num_force_unchokes consecutivi non superino il valore di i
         *  e sottrae unchoke_since_last, che e' il numero di unchoke recentemente eseguiti al di fuori
         *  dell'intervallo di 10 secondi, a causa di connection_lost.
         *  (i + chokingCount % 3) / 3 e' uguale a i se sommato su 3 chokingCount consecutivi
         *
         */

        num_force_unchokes = max(0, (int) (((i + chokingCount % 3) / 3) - unchokes_since_last));

        EV << "num_force_unchokes = " << num_force_unchokes<< "\n";

    }
    //else num_force_unchoke=0

    //in ogni caso proseguo l'algoritmo
    vector<RateStruct *> preferred;  //lista dei peer preferiti

    long new_limit = chokingCount - 3;

    /*
     * Vengono ordinate prima le connessioni unchoked recentemente
     * (unchoke_time >new limit, cioe' negli ultimi 2 round) oppure che
     * hanno dati da inviare poi le altre in ordine di upload rate
     */

    int pos1=0;
    for (list<int>::iterator i = connections.begin(); i!=connections.end();++i,pos1++){
        if((!othersPeer[*i]->am_choking) &&  othersPeer[*i]->peer_interested){ // se il peer non e' soffocato ed e' interessato

            /*
             * se non ho alcuna richiesta pendente dal peer remoto, non posso inviargli dati immediatamente
             */

            bool pendingReq=false;
            bool ougoingPiece=false;
            int index=-1;
            int n=1;

            for (list<BitTorrentMsg *>::iterator ms = outgoing.begin(); ms!=outgoing.end();++ms){
                if (((*ms)->getDestination()==(*i)) && (dynamic_cast<Piece *>(*ms))) {
                    index=outgoing.size()-n;
                    ougoingPiece=true;
                }
                n++;
            }


            for (list<MsgStruct *>::iterator ms = incoming.begin(); ms!=incoming.end();++ms){
                    if (((*ms)->sourcePeer==(*i)) && ((*ms)->isRequest)) {
                        pendingReq=true;
                        break;
                    }
            }


            RateStruct * ordDown = new RateStruct();

            ordDown->peer=*i;

            ordDown->pos=pos1;

            if(othersPeer[*i]->downloadRate.size()<2){ //sono passati solo 10 secondi, considero quelli

                ordDown->rateLastTwenty=*(othersPeer[*i]->downloadRate.rbegin());
                ordDown->rateLastThirty=*(othersPeer[*i]->downloadRate.rbegin());
                ordDown->rateCancelTwenty=*(othersPeer[*i]->canceledRate.rbegin());

            }
            else if(othersPeer[*i]->downloadRate.size()==2){ // sono passati 20 secondi
                ordDown->rateLastTwenty=*(othersPeer[*i]->downloadRate.rbegin())+*(++(othersPeer[*i]->downloadRate.rbegin()));
                ordDown->rateCancelTwenty=*(othersPeer[*i]->canceledRate.rbegin())+*(++(othersPeer[*i]->canceledRate.rbegin()));
                ordDown->rateLastThirty=*(othersPeer[*i]->downloadRate.rbegin())+*(++(othersPeer[*i]->downloadRate.rbegin()));


            }else{ // sono gia' passati 30 secondi

                ordDown->rateLastTwenty=*(othersPeer[*i]->downloadRate.rbegin())+*(++(othersPeer[*i]->downloadRate.rbegin()));
                ordDown->rateCancelTwenty=*(othersPeer[*i]->canceledRate.rbegin())+*(++(othersPeer[*i]->canceledRate.rbegin()));
                ordDown->rateLastThirty=*(othersPeer[*i]->downloadRate.rbegin())+*(++(othersPeer[*i]->downloadRate.rbegin())) + *(++(++(othersPeer[*i]->downloadRate.rbegin())));
            }


            if((othersPeer[*i]->unchoking_time > new_limit) || (ougoingPiece && index<2) || (pendingReq && outgoing.size()<2)){

                ordDown->unchoked_time=othersPeer[*i]->unchoking_time;

            }else{

                ordDown->unchoked_time=1;
            }

            preferred.push_back(ordDown);

        }
    }
    //num_kept e' il numero di connessioni da mantenere. Le altre connessioni possono essere choked
    int num_kept = par("max_uploads").longValue() - num_force_unchokes;
    EV << "num_kept = " << num_kept << "\n";
    assert(num_kept >= 0);  // num_kept sempre >=0
    stable_sort(preferred.begin(), preferred.end(), RateStruct::seederComparator);
    if(preferred.size()>(unsigned)num_kept){
        for(vector<RateStruct *>::iterator it = (preferred.begin()+(num_kept)); it!=preferred.end();it++){
            delete (*it);
        }
       preferred.erase(preferred.begin()+(num_kept),preferred.end());
    }
    vector<int> mask(connections.size(),0); //inizializzo a zero l'array mask di lunghezza connections
    for(vector<RateStruct *>::iterator it = preferred.begin();it!=preferred.end(); ++it){
         mask[(*it)->pos] = 1;
     }
    //num_nonpref e' il numero di connessioni che saranno soffocate
    int num_nonpref = par("max_uploads").longValue() - preferred.size();

    EV << "num_nonpref = " << num_nonpref << "\n";


    EV << "Peer " << peerID << ": la lista preferred dei peer e' cosi' composta:\n";
    for(vector<RateStruct *>::iterator it = preferred.begin();it!=preferred.end();++it){

        EV << (*it)->peer << " con unchoking time=" << (*it)->unchoked_time <<" e rate di download = " << (*it)->rateLastTwenty << ", " << endl;

    }

    EV << "\n";


    while (!preferred.empty()) {
        delete preferred.back();
        preferred.pop_back();
    }

    /**
     * unchoke_since_last e' il numero di unchoke dall'ultima chiamata regolare (quella che avviene ogni 10s
     * e non quella eseguita da una connection close) a rechoke_seed().
     * Appena una chiamata regolare a rechoke_seed() e' eseguita (force_new_unchkes=true)
     * unchoke_since_last e' resettata a 0
     */
    if(force_new_unchokes){
        unchokes_since_last = 0;
    }
    else{
        unchokes_since_last += num_nonpref;
    }

    EV << "unchokes_since_last = " << unchokes_since_last << "\n";


    int last_unchoked = -1;
    int pos=0;
    for (list<int>::iterator i = connections.begin(); i!=connections.end();++i,pos++){

        EV << "connection -> " << *i << ", pos ->" << pos << ", mask[pos] -> " << mask[pos] <<"\n";

        if(!mask[pos]){ // se non e' una connessione da tenere

            EV << "il peer " << *i << " non e' tra le connessioni da mantenere unchoked\n";


            if(!(othersPeer[*i]->peer_interested)){ // se non e' interessato

                EV << "il peer " << *i << " non e' interessato\n";


                if(!(othersPeer[(*i)]->am_choking)){

                    EV << "Peer " << peerID << ": Faccio il choking del peer " << (*i) << "\n";

                    othersPeer[*i]->am_choking=true;
                    othersPeer[*i]->pending_unchoking=false;

                    // rimuovo dalla coda in ingresso le richieste che mi ha fatto il peer
                    for (list<MsgStruct *>::iterator ms = incoming.begin(); ms!=incoming.end();){
                        if (((*ms)->sourcePeer==(*i)) && ((*ms)->isRequest)) {

                            Request * msg = dynamic_cast<Request *>((*ms)->popMsg());

                            list<Request*> * queue = &(othersPeer[*i]->requestsQueue);

                            for (list<Request*>::iterator it = (*queue).begin(); it!=(*queue).end();++it){

                                if((*it)->getBlockIndex() == msg->getBlockIndex() &&
                                        (*it)->getPieceIndex() == msg->getPieceIndex()){
                                    delete(*it);
                                    othersPeer[*i]->requestsQueue.erase(it++);
                                    break;
                                }

                            }

                            delete (*ms); // distruggo la struttura E l'eventuale messaggio contenuto

                            ms= incoming.erase(ms); // rimuovo dalla coda


                        } else {
                            ++ms; // avanzo
                        }
                    }

                    // rimuovo dalla coda in uscita i pezzi che sto mandando al peer
                    for (list<BitTorrentMsg *>::iterator ms = outgoing.begin(); ms!=outgoing.end();){
                        if (((*ms)->getDestination()==(*i)) && (dynamic_cast<Piece *>(*ms))) {


                            Piece * msg = dynamic_cast<Piece *>(*ms);

                            list<Request*> * queue = &(othersPeer[*i]->requestsQueue);

                            for (list<Request*>::iterator it = (*queue).begin(); it!=(*queue).end();++it){

                                if((*it)->getBlockIndex() == msg->getBlockIndex() &&
                                        (*it)->getPieceIndex() == msg->getPieceIndex()){
                                    delete(*it);
                                    othersPeer[*i]->requestsQueue.erase(it++);
                                    break;
                                }

                            }

                            delete (*ms); // distruggo il messaggio contenuto

                            ms = outgoing.erase(ms); // rimuovo dalla coda


                        } else {
                            ++ms; // avanzo
                        }
                    }


                    // invio al peer il messaggio di Choking
                    Choke * chokeMsg= new Choke();
                    forwardMessage(chokeMsg,(*i),"Choke");


                }
            }
            else if (othersPeer[(*i)]->am_choking){ //altrimenti se interested e choked
                EV << "il peer " << *i << " e' interessato\n";

                if(num_nonpref > 0){

                        EV << "Peer " << peerID << ": Faccio l'unchoking del peer " << ((*i)) << "\n";

                        othersPeer[(*i)]->am_choking=false;
                        othersPeer[(*i)]->pending_unchoking=false;
                        othersPeer[(*i)]->unchoking_time=chokingCount;

                        // invio al peer il messaggio di Unchoking
                        Unchoke * unchokeMsg= new Unchoke();
                        forwardMessage(unchokeMsg,(*i),"Unchoke");

                        num_nonpref -= 1;

                        if(num_nonpref == 0){
                            last_unchoked = *i;
                        }


                }
            }else{ // se interested ed unchoked

                EV << "il peer " << *i << " e' gia' interested ed unchoked\n";


                if(num_nonpref == 0){
                    if(!(othersPeer[(*i)]->am_choking)){

                        EV << "Peer " << peerID << ": Faccio il choking del peer " << (*i);

                        othersPeer[*i]->am_choking=true;
                        othersPeer[*i]->pending_unchoking=false;


                        // rimuovo dalla coda in ingresso le richieste che mi ha fatto il peer
                        for (list<MsgStruct *>::iterator ms = incoming.begin(); ms!=incoming.end();){
                            if (((*ms)->sourcePeer==(*i)) && ((*ms)->isRequest)) {

                                Request * msg = dynamic_cast<Request *>((*ms)->popMsg());


                                list<Request*> * queue = &(othersPeer[*i]->requestsQueue);

                                for (list<Request*>::iterator it = (*queue).begin(); it!=(*queue).end();++it){

                                    if((*it)->getBlockIndex() == msg->getBlockIndex() &&
                                            (*it)->getPieceIndex() == msg->getPieceIndex()){
                                        delete(*it);
                                        othersPeer[*i]->requestsQueue.erase(it++);
                                        break;
                                    }

                                }

                                delete (*ms); // distruggo la struttura E l'eventuale messaggio contenuto

                                ms= incoming.erase(ms); // rimuovo dalla coda


                            } else {
                                ++ms; // avanzo
                            }
                        }

                        // rimuovo dalla coda in uscita i pezzi che sto mandando al peer
                        for (list<BitTorrentMsg *>::iterator ms = outgoing.begin(); ms!=outgoing.end();){
                            if (((*ms)->getDestination()==(*i)) && (dynamic_cast<Piece *>(*ms))) {


                                Piece * msg = dynamic_cast<Piece *>(*ms);

                                list<Request*> * queue = &(othersPeer[*i]->requestsQueue);

                                for (list<Request*>::iterator it = (*queue).begin(); it!=(*queue).end();++it){

                                    if((*it)->getBlockIndex() == msg->getBlockIndex() &&
                                            (*it)->getPieceIndex() == msg->getPieceIndex()){
                                        delete(*it);
                                        othersPeer[*i]->requestsQueue.erase(it++);
                                        break;
                                    }

                                }

                                delete (*ms); // distruggo il messaggio contenuto

                                ms = outgoing.erase(ms); // rimuovo dalla coda


                            } else {
                                ++ms; // avanzo
                            }
                        }


                        // invio al peer il messaggio di Choking
                        Choke * chokeMsg= new Choke();
                        forwardMessage(chokeMsg,(*i),"Choke");

                    }
                }else{
                    num_nonpref -= 1;
                    if(num_nonpref == 0){
                        last_unchoked = *i; //salvo il last_unchoked
                    }
                }
            }
        }
    }
    EV << "last_unchoked = " << last_unchoked<< "\n";
    if(last_unchoked !=-1){
        list<int> before;
        list<int>::iterator it =  connections.begin();
        advance(it,last_unchoked + 1);
        before.splice(before.begin(),connections,it,connections.end());
        connections.splice(connections.begin(),before);

        EV << "Peer " << peerID << ": la lista connections e' cosi' composta:\n";
        for (list<int>::iterator i = connections.begin(); i!=connections.end();++i){

            EV << *i <<", ";

        }
        EV << "\n";
    }

}

/**
 * nuove connessioni sono inserite in una posizione random nella lista connections
 */
 void Peer::connection_made(int peer){
         EV << "Peer " << peerID << ": aggiungo una nuova connessione a "<< peer << "\n";
        int p = intuniform(0,connections.size());
        list<int>::iterator it=connections.begin();
        advance(it,p);
        connections.insert(it, peer);
 }

 void Peer::connection_lost(int peer){
     if(peer!=-1){
        EV << "Peer " << peerID << ": rimuovo la connessione a " << peer << "\n";
        connections.remove(peer); //rimuovo la connessione
        RemotePeer * rPeer= othersPeer[peer];
        bool peerIntUnch=(rPeer->peer_interested && (!(rPeer->am_choking)));
        rPeer->timeout= NULL;
          delete rPeer;
        othersPeer.erase(peer);
        if(peerIntUnch){
            rechoke();
        }
     }
     else if(peer==-1){
         rechoke();
     }
 }



/**
 * Restituisce i peer remoti unchoked
 */
set<int> * Peer::getUnchokedPeers(){

    set<int> * unchokedPeers = new set<int>(); // TODO ricordarsi di farne il delete

    for (map<int, RemotePeer *>::iterator i = othersPeer.begin(); i != othersPeer.end(); ++i){

        if(i->second->am_choking==false)
            unchokedPeers->insert(i->first);
    }

    return unchokedPeers;


}

/**
 * restituisce i peer remoti choked (seeder esclusi)
 */
set<int> * Peer::getChokedPeers(){

    set<int> * chokedPeers=new set<int>(); // TODO ricordarsi di farne il delete

    for (map<int, RemotePeer *>::iterator i = othersPeer.begin(); i != othersPeer.end(); ++i){

            if ( (i->second->am_choking) && !i->second->btFile->haveTorrent() ) {
                chokedPeers->insert(i->first);
            }
        }

    return chokedPeers;

}


// // //
// CLASSI "MINORI" (EX STRUCT)

long RateStruct::countConstructors=0;
long RateStruct::countDestructors=0;
RateStruct::RateStruct() {
    countConstructors++;
}
RateStruct::~RateStruct() {
    countDestructors++;
}

long MsgStruct::countConstructors=0;
long MsgStruct::countDestructors=0;
MsgStruct::MsgStruct() {
    isRequest= 0;
    pieceIndex= 0;
    blockIndex= 0;
    sourcePeer= 0;
    this->msg= NULL;
    countConstructors++;
}
MsgStruct::~MsgStruct() {
    if (msg!=NULL) {
        delete msg;
    }
    countDestructors++;
}
BitTorrentMsg * MsgStruct::popMsg() {
    if (msg!=NULL) {
        BitTorrentMsg *ret= msg;
        msg= NULL;
        return ret;
    } else {
        throw std::runtime_error("MsgStruct::popMsg(), ma non e' presenta alcun messaggio");
    }
}
BitTorrentMsg * MsgStruct::getMsg() {
    return msg;
}
void MsgStruct::pushMsg(BitTorrentMsg * msg) {
    if (msg!=NULL) {
        this->msg= msg;
    } else {
        throw std::runtime_error("MsgStruct::pushMsg(), ma era gia' presente un messaggio");
    }
}

long PieceScore::countConstructors=0;
long PieceScore::countDestructors=0;
PieceScore::PieceScore(int index, int score) {
    this->index= index;
    this->score= score;
    countConstructors++;
}
PieceScore::~PieceScore() {
    countDestructors++;
}

long RemotePeer::countConstructors=0;
long RemotePeer::countDestructors=0;
RemotePeer::RemotePeer() {
    btFile= NULL;
    timeout= NULL;

    countConstructors++;
}
RemotePeer::~RemotePeer() {

    if (btFile!=NULL) delete btFile;
    while (!requestsQueue.empty()) {
        delete requestsQueue.front();
        requestsQueue.pop_front();
    }
    if (timeout!=NULL) delete timeout;

    countDestructors++;
}


/*
 * Algoritmo di chocking:
 *
 * - ogni peer periodicamente valuta, per ogni vicino, la velocita' di download da quel vicino nel round
 *   precedente e decide quali vicini 'strozzare' (choked)
 *
 * - Al massimo 4 peer unchoked ed interested:
 *
 *  - Regular Unchocked Peer: ogni 10 secondi (1 round = 10 secondi) i peer remoti interessati al peer
 *    locale sono ordinati rispetto alla velocita' con cui hanno inviato i dati al peer
 *    locale ed i 3 piu' veloci vengono posti in stato unchocked.
 *
 *  - Optimistic Unchocked Peer: ogni 30 secondi (ogni 3 round) si sceglie un peer ulteriore in modo casuale
 *    e si effettua l'unchoking di quel peer.
 *
 * Leecher choking:
 *
 * - Ogni 3 round:
 *      - si sceglie in maniera casuale un peer tra quelli chocked ed interested e se ne effettua l'unchocking
 *        (optimisting unchoking)
 * - Ad ogni round, si ordinano tutti i peer interessati che hanno inviato almeno un blocco di dati negli
 *   ultimi tre round, secondo la loro velocita' di upload
 *      - la velocita' di upload viene calcolata mediante una media sugli ultimi 20 secondi
 *      - si effettua l'unchoking dei 3 peer piu' veloci (regular unchoke)
 *      - se l'optimistic unchoked peer non fa parte dei tre peer prescelti,l'agoritmo termina.
 *      - se l'optimistic unchoked peer fa parte dell'insieme dei regular unchoked peers, si sceglie in maniera
 *        casuale un nuovo peer su cui fare l'optimistic unchoking
 *             - se il peer scelto e' interessato, ne viene effettuato l'unchoking e l'algoritmo termina
 *             - se non e' interessato, ne viene effettuato comunque l'unchoking e viene ricercato un altro
 *               peer su cui effettuare l'optimistic unchoking (permesso unchoking di un peer non
 *               interessato. L'algoritmo viene immediatamente rieseguito nel caso in cui il peer diventi
 *               interessato).
 * - In un round effettuato l'unchoke di 4 peer interessati e di un insieme di peer non interessati.
 *   L'algoritmo viene invocato:
 *      - regolarmente, ogni 10 secondi
 *      - quando un peer unchoked diventa interessato e viceversa
 *      - quando un peer viene eliminato dal Peer Set,ad esempio perche' ha lasciato volontariamente la rete
 *
 *
 * Antisnubbing:
 *
 * - Snubbing: se un peer P non riceve pezzi da Q, P non effettua upload verso Q, a meno che Q non sia scelto
 *   da un optimistic upload
 * - Un peer e' definito snubbed se tutti gli altri hanno effettuato il choking su di esso
 *       - un peer snubbed non effettua l'uploading verso alcun altro peer
 *       - il downlaod dei contenuti viene bloccato per i peer snubbed, finche' il peer non viene scelto come
 *         target di un optimistic unchoke
 *
 * Occorre aumentare il numero di optimistic unchoke con l'obiettivo di trovare un peer che effettui l'upload
 * verso lo snubbed peer
 *
 *
 * Seeder Choking Algorithm:
 *
 * - peer (seeder) ordina i peer remoti rispetto alla loro velocita' di download (invece che rispetto quella
 *   di upload) perche' il seeder non scarica nulla dagli altri)
 *
 */

/*
void Peer::chokingAlgorithm(int sec){


        if(areAllSeeder()){ // tutti i peer attivi sono seeder (io compreso)

            chokingMsg=NULL;
            optPeer=-1;

            EV << "Peer "<< peerID << ": Siamo tutti seeder (non riavvio timer per il choking)\n";

#ifndef NO_COUT
            cout << "> Peer " << peerID << ": Siamo tutti seeder (non riavvio timer per il choking)\n";
            cout.flush();
#endif

            return;  // non devo riavviare il timer

        }

        if(!someoneIsInterested()){ // nessuno e' interessato a me (possiedono gia' i pezzi di file che io ho)

            chokingMsg=NULL;
            optPeer=-1;

            EV << "Peer "<< peerID << ": Nessuno e' interessato a me (non riavvio timer per il choking)\n";

#ifndef NO_COUT
            cout << "> Peer " << peerID << ": Nessuno e' interessato a me (non riavvio timer per il choking)\n";
            cout.flush();
#endif

            return;  // non devo riavviare il timer
        }


        bool seeder = false;
        vector<RateStruct *> downVec;
        vector<RateStruct *> upVec;
        vector<RateStruct *> * rateVec;

        if(myFile->haveTorrent()){ // se ho tutto il file sono un seeder

            seeder=true;

            for (map<int, RemotePeer *>::iterator i = othersPeer.begin(); i != othersPeer.end(); ++i){


                RateStruct * ordDown = new RateStruct();

                ordDown->peer=i->first;

                if(i->second->downloadRate.size()<2){ //sono passati solo 10 secondi, considero quelli

                    ordDown->rateLastTwenty=*(i->second->downloadRate.rbegin());
                    ordDown->rateLastThirty=*(i->second->downloadRate.rbegin());
                    ordDown->rateCancelTwenty=*(i->second->canceledRate.rbegin());

                }
                else if(i->second->downloadRate.size()==2){ // sono passati 20 secondi
                    ordDown->rateLastTwenty=*(i->second->downloadRate.rbegin())+*(++(i->second->downloadRate.rbegin()));
                    ordDown->rateCancelTwenty=*(i->second->canceledRate.rbegin())+*(++(i->second->canceledRate.rbegin()));
                    ordDown->rateLastThirty=*(i->second->downloadRate.rbegin())+*(++(i->second->downloadRate.rbegin()));


                }else{ // sono gia' passati 30 secondi

                    ordDown->rateLastTwenty=*(i->second->downloadRate.rbegin())+*(++(i->second->downloadRate.rbegin()));
                    ordDown->rateCancelTwenty=*(i->second->canceledRate.rbegin())+*(++(i->second->canceledRate.rbegin()));
                    ordDown->rateLastThirty=*(i->second->downloadRate.rbegin())+*(++(i->second->downloadRate.rbegin())) + *(++(++(i->second->downloadRate.rbegin())));
                }

                downVec.push_back(ordDown);

            }

            // il vettore ora e' ordinato secondo i peer che hanno un download rate piu' alto
            stable_sort(downVec.begin(), downVec.end(), RateStruct::comparator);

        }else{ //leecher

            // ordino tutti i peer interessati in base alla velocita' di upload degli ultimi 20 secondi

            for (map<int, RemotePeer *>::iterator i = othersPeer.begin(); i != othersPeer.end(); ++i){


                RateStruct * ordUp = new RateStruct();

                ordUp->peer=i->first;
                if(i->second->uploadRate.size()<2){ //sono passati solo 10 secondi, considero quelli

                    ordUp->rateLastTwenty=*(i->second->uploadRate.rbegin());
                    ordUp->rateLastThirty=*(i->second->uploadRate.rbegin());
                    ordUp->rateCancelTwenty=*(i->second->canceledRate.rbegin());

                }
                else if(i->second->uploadRate.size()==2){ // sono passati 20 secondi
                    ordUp->rateLastTwenty=*(i->second->uploadRate.rbegin())+*(++(i->second->uploadRate.rbegin()));
                    ordUp->rateCancelTwenty=*(i->second->canceledRate.rbegin())+*(++(i->second->canceledRate.rbegin()));
                    ordUp->rateLastThirty=*(i->second->uploadRate.rbegin())+*(++(i->second->uploadRate.rbegin()));


                }else { // sono gia' passati 30 secondi

                    ordUp->rateLastTwenty=*(i->second->uploadRate.rbegin())+*(++(i->second->uploadRate.rbegin()));
                    ordUp->rateCancelTwenty=*(i->second->canceledRate.rbegin())+*(++(i->second->canceledRate.rbegin()));
                    ordUp->rateLastThirty=*(i->second->uploadRate.rbegin())+*(++(i->second->uploadRate.rbegin())) + *(++(++(i->second->uploadRate.rbegin())));
                }

                upVec.push_back(ordUp);

            }


            if(!maliciousEnabled) {            // il vettore viene ordinato secondo i peer che hanno un upload rate piu' alto
                stable_sort(upVec.begin(), upVec.end(), RateStruct::comparator);
            } else {  // maliciousEnabled
                if(par("unchokeWithCancelNum").boolValue()){
                // il vettore viene ordinato secondo i peer che hanno un upload rate piu' alto ma che hanno ricevuto meno Cancel
                    stable_sort(upVec.begin(), upVec.end(),RateStruct::endGameComparator);
                }
                else {
                    stable_sort(upVec.begin(), upVec.end(), RateStruct::comparator);
                }

            }

        }



        if(seeder)
            rateVec= &downVec;
        else
            rateVec = &upVec;



        EV << "Peer "<< peerID << (seeder?" (Seeder) ->":" (Leecher) ->") << " Peer in ordine di "<< (seeder?"download":"upload") << " rate (ultimi 20 secondi):\n";

        int j=0;

        for(vector<RateStruct *>::iterator i = (*rateVec).begin(); i != (*rateVec).end(); ++i){

            if (othersPeer[(*i)->peer]->btFile->haveTorrent()) {
                // se e' un seeder, lo salto
                EV << "Peer "<< peerID << ": il peer --> " << ((*i)->peer) << " e' un seeder quindi non lo considero \n";
                continue;
            }

            bool rateLastThirty;
            if (seeder) {
                 //
                 // Considero anche quelli a cui non ho mai inviato niente: serve per permettere di iniziare a scaricare
                 // anche da parte dei nuovi peer. Se cosi' non fosse, nessun peer riuscirebbe a iniziare il download dal seeder.
                 //
                rateLastThirty= true;
            } else {
                rateLastThirty= ((*i)->rateLastThirty)>1; // mi deve avere inviato almeno un blocco negli ultimi 3 round (30 secondi)
            }
              if((j<(par("simultaneousUpload").longValue())-1) && (rateLastThirty) && (othersPeer[(*i)->peer]->peer_interested)){

                  // rimuovo 1 a simultaneousUpload perche' va aggiunto il peer interessato selezionato
                  // con l'optimistic unchoking [ad esempio, se sono al max 4 upload contemporanei (3 peer saranno presi da qui e uno con l'opimistic Unchoking)]

                  //faccio l'unchoking di questo peer se non e' gia' nello stato unchoked

                  EV << "Peer "<< peerID << ": regular unchoked peer --> " << ((*i)->peer) << " (rate " << ((*i)->rateLastTwenty) << ")\n";

                  if (othersPeer[(*i)->peer]->am_choking) {

                      EV << "Faccio l'unchoking del peer " << ((*i)->peer) << "\n";

                      othersPeer[(*i)->peer]->am_choking=false;
                      othersPeer[(*i)->peer]->pending_unchoking=false;

                      // invio al peer il messaggio di Unchoking
                      Unchoke * unchokeMsg= new Unchoke();
                      forwardMessage(unchokeMsg,(*i)->peer,"Unchoke");


                  }

                  if((*i)->peer==optPeer){ //se il peer scelto e' gia' stato selezionato tramite l'optimistic unchoking
                      // il vecchio peer che era stato selezionato con optimistic unchoking diventa un peer scelto con unchoking tradizionale
                      // e in seguito dovro' selezionare un nuovo optimistic unchoked peer
                      optPeer=-1;
                  }
                  j++;



               }

              else{ // gli altri peer piu' lenti o che non mi hanno inviato niente negli ultimi 20 secondi vanno nello stato Choked


                  EV << "Peer " << peerID << ": choked peer --> " << ((*i)->peer) << " (rate " << ((*i)->rateLastTwenty) << ")\n";


                  if(!(othersPeer[(*i)->peer]->am_choking)){

                    EV << "Faccio il choking del peer " << ((*i)->peer);

                    othersPeer[(*i)->peer]->am_choking=true;

                    // rimuovo dalla coda in ingresso le richieste che mi ha fatto il peer
                    for (list<MsgStruct *>::iterator ms = incoming.begin(); ms!=incoming.end();){
                        if (((*ms)->sourcePeer==(*i)->peer) && ((*ms)->isRequest)) {

                            delete (*ms); // distruggo la struttura E l'eventuale messaggio contenuto

                            ms= incoming.erase(ms); // rimuovo dalla coda


                        } else {
                            ++ms; // avanzo
                        }
                    }

                    // rimuovo dalla coda in uscita i pezzi che sto mandando al peer
                    for (list<BitTorrentMsg *>::iterator ms = outgoing.begin(); ms!=outgoing.end();){
                        if (((*ms)->getDestination()==(*i)->peer) && (dynamic_cast<Piece *>(*ms))) {

                            delete (*ms); // distruggo il messaggio contenuto

                            ms = outgoing.erase(ms); // rimuovo dalla coda


                        } else {
                            ++ms; // avanzo
                        }
                    }


                    // invio al peer il messaggio di Choking
                    Choke * chokeMsg= new Choke();
                    forwardMessage(chokeMsg,(*i)->peer,"Choke");


                  }


              }


        }

        EV << "\n";

        if (optPeer == -1 || sec==30) {
           optimisticUnchoking();
        }



        if(chokingMsg!=NULL && chokingMsg->isScheduled())
            cancelAndDelete(chokingMsg); //  se era nella coda degli auto-messaggi verra' rimosso


        chokingMsg=new ChokingAlgorithm("ChokingAlgorithm");
        if(sec==30)
            chokingMsg->setSec(10);
        else
            chokingMsg->setSec(sec+10);

        // riavvio il timer
        scheduleAt(simTime()+simTime().parse("10s"), chokingMsg);

        while (!rateVec->empty()) {
            delete rateVec->back();
            rateVec->pop_back();
        }
}*/


 /**
  * algoritmo di optimistic Unchoking
  */
 /*void Peer::optimisticUnchoking(){

     EV << "Peer " << peerID << ": eseguo l'optimistic unchoking\n";


     bool found=false;

     // dall'insieme dei choked peer scelgo un peer su cui effettuare l'unchoking

     set<int> * choked = getChokedPeers(); // ottengo i peer che ho soffocato

     //azzero gli unchoking pendenti
     for (set<int>::iterator i = choked->begin(); i != choked->end(); ++i){

         othersPeer[*i]->pending_unchoking=false;

     }


     while(!found){

         int size = choked->size();

         if(size==0) break;  // se non ci sono choked peer, termino l'algoritmo

         int rand = intuniform(0,size-1);

         set<int>::iterator it(choked->begin());

         advance(it,rand);



         if(othersPeer[*it]->peer_interested){ // se il peer e' interessato termino l'algoritmo di optimistic unchoking

             othersPeer[*it]->am_choking=false;

             // invio al peer il messaggio di Unchoking

             Unchoke * unchokeMsg= new Unchoke();
             forwardMessage(unchokeMsg,*it,"Unchoke");


             // da adesso questo peer fa parte dell'insieme di peer unchoked
             EV << "Peer " << peerID << ": Ho segnato il peer " << (*it) << " come optimistic unchoked peer\n";
             optPeer= *it;
             found=true;
         }

         else{

             othersPeer[*it]->pending_unchoking=true;
         }


         // in ogni caso non considero piu' questo peer
         choked->erase(it);

     }

     delete choked;

     if(!found)
         optPeer=-1;

 }*/



// // //
//

}; // namespace
